Commit 876973a8 authored by hansbrix's avatar hansbrix

html->wiki conversion script

parent c4ca5964
......@@ -2,12 +2,14 @@
Doctrine_Db_Profiler is an eventlistener for Doctrine_Db. It provides flexible query profiling. Besides the sql strings
the query profiles include elapsed time to run the queries. This allows inspection of the queries that have been performed without the
need for adding extra debugging code to model classes.
<br \><br \>
Doctrine_Db_Profiler can be enabled by adding it as an eventlistener for Doctrine_Db.
<br \><br \>
<?php
renderCode("<?php
?>");
?>
<code type="php">
?></code>
[<b>Note</b>: The term 'Transaction' doesnt refer to database transactions here but to the general meaning of this term]<br />
[<b>Note</b>: This component is in <b>Alpha State</b>]<br />
<br />
[**Note**: The term 'Transaction' doesnt refer to database transactions here but to the general meaning of this term]
[**Note**: This component is in **Alpha State**]
Locking is a mechanism to control concurrency. The two most well known locking strategies
are optimistic and pessimistic locking. The following is a short description of these
two strategies from which only pessimistic locking is currently supported by Doctrine.<br />
<br />
<b>Optimistic Locking:</b><br />
two strategies from which only pessimistic locking is currently supported by Doctrine.
**Optimistic Locking:**
The state/version of the object(s) is noted when the transaction begins.
When the transaction finishes the noted state/version of the participating objects is compared
to the current state/version. When the states/versions differ the objects have been modified
by another transaction and the current transaction should fail.
This approach is called 'optimistic' because it is assumed that it is unlikely that several users
will participate in transactions on the same objects at the same time.<br />
<br />
<b>Pessimistic Locking:</b><br />
will participate in transactions on the same objects at the same time.
**Pessimistic Locking:**
The objects that need to participate in the transaction are locked at the moment
the user starts the transaction. No other user can start a transaction that operates on these objects
while the locks are active. This ensures that the user who starts the transaction can be sure that
noone else modifies the same objects until he has finished his work.<br />
<br />
noone else modifies the same objects until he has finished his work.
Doctrine's pessimistic offline locking capabilities can be used to control concurrency during actions or procedures
that take several HTTP request and response cycles and/or a lot of time to complete.
Roman Borschel - romanb at #doctrine (freenode)<br />
Roman Borschel - romanb at #doctrine (freenode)
Don't hesitate to contact me if you have questions, ideas, ect.
......@@ -3,16 +3,22 @@ You can think of this validation as a gateway that needs to be passed right befo
persistent data store. The definition of these business rules takes place at the record level, that means
in your active record model classes (classes derived from Doctrine_Record).
The first thing you need to do to be able to use this kind of validation is to enable it globally.
This is done through the Doctrine_Manager (see the code below).<br />
<br />
Once you enabled validation, you'll get a bunch of validations automatically:<br />
<br />
This is done through the Doctrine_Manager (see the code below).
Once you enabled validation, you'll get a bunch of validations automatically:
- Data type validations: All values assigned to columns are checked for the right type. That means
if you specified a column of your record as type 'integer', Doctrine will validate that
any values assigned to that column are of this type. This kind of type validation tries to
be as smart as possible since PHP is a loosely typed language. For example 2 as well as "7"
are both valid integers whilst "3f" is not. Type validations occur on every column (since every
column definition needs a type).<br /><br />
column definition needs a type).
- Length validation: As the name implies, all values assigned to columns are validated to make
sure that the value does not exceed the maximum length.
......
The type and length validations are handy but most of the time they're not enough. Therefore
Doctrine provides some mechanisms that can be used to validate your data in more detail.<br />
<br />
Doctrine provides some mechanisms that can be used to validate your data in more detail.
Validators: Validators are an easy way to specify further validations. Doctrine has a lot of predefined
validators that are frequently needed such as email, country, ip, range and regexp validators. You
find a full list of available validators at the bottom of this page. You can specify which validators
apply to which column through the 4th argument of the hasColumn() method.
If that is still not enough and you need some specialized validation that is not yet available as
a predefined validator you have three options:<br />
<br />
- You can write the validator on your own.<br />
- You can propose your need for a new validator to a Doctrine developer.<br />
- You can use validation hooks.<br />
<br />
a predefined validator you have three options:
- You can write the validator on your own.
- You can propose your need for a new validator to a Doctrine developer.
- You can use validation hooks.
The first two options are advisable if it is likely that the validation is of general use
and is potentially applicable in many situations. In that case it is a good idea to implement
a new validator. However if the validation is special it is better to use hooks provided by Doctrine:<br />
<br />
- validate() (Executed every time the record gets validated)<br />
- validateOnInsert() (Executed when the record is new and gets validated)<br />
- validateOnUpdate() (Executed when the record is not new and gets validated)<br />
<br />
a new validator. However if the validation is special it is better to use hooks provided by Doctrine:
- validate() (Executed every time the record gets validated)
- validateOnInsert() (Executed when the record is new and gets validated)
- validateOnUpdate() (Executed when the record is not new and gets validated)
If you need a special validation in your active record
you can simply override one of these methods in your active record class (a descendant of Doctrine_Record).
Within thess methods you can use all the power of PHP to validate your fields. When a field
doesnt pass your validation you can then add errors to the record's error stack.
The following code snippet shows an example of how to define validators together with custom
validation:<br />
validation:
<code type="php">
class User extends Doctrine_Record {
......
Now that you know how to specify your business rules in your models, it is time to look at how to
deal with these rules in the rest of your application.<br />
<br />
Implicit validation:<br />
deal with these rules in the rest of your application.
Implicit validation:
Whenever a record is going to be saved to the persistent data store (i.e. through calling $record->save())
the full validation procedure is executed. If errors occur during that process an exception of the type
Doctrine_Validator_Exception will be thrown. You can catch that exception and analyze the errors by
......@@ -10,15 +13,20 @@ an ordinary array with references to all records that did not pass validation. Y
further explore the errors of each record by analyzing the error stack of each record.
The error stack of a record can be obtained with the instance method Doctrine_Record::getErrorStack().
Each error stack is an instance of the class Doctrine_Validator_ErrorStack. The error stack
provides an easy to use interface to inspect the errors.<br />
<br />
Explicit validation:<br />
provides an easy to use interface to inspect the errors.
Explicit validation:
You can explicitly trigger the validation for any record at any time. For this purpose Doctrine_Record
provides the instance method Doctrine_Record::isValid(). This method returns a boolean value indicating
the result of the validation. If the method returns FALSE, you can inspect the error stack in the same
way as seen above except that no exception is thrown, so you simply obtain
the error stack of the record that didnt pass validation through Doctrine_Record::getErrorStack().<br />
<br />
the error stack of the record that didnt pass validation through Doctrine_Record::getErrorStack().
The following code snippet shows an example of handling implicit validation which caused a Doctrine_Validator_Exception.
<code type="php">
......
There are couple of availible Cache attributes on Doctrine:
<ul>
<li \>Doctrine::ATTR_CACHE_SIZE
<ul>
<li \> Defines which cache container Doctrine uses
<li \> Possible values: Doctrine::CACHE_* (for example Doctrine::CACHE_FILE)
</ul>
<li \>Doctrine::ATTR_CACHE_DIR
<ul>
<li \> cache directory where .cache files are saved
<li \> the default cache dir is %ROOT%/cachedir, where
* Doctrine::ATTR_CACHE_SIZE
* Defines which cache container Doctrine uses
* Possible values: Doctrine::CACHE_* (for example Doctrine::CACHE_FILE)
* Doctrine::ATTR_CACHE_DIR
* cache directory where .cache files are saved
* the default cache dir is %ROOT%/cachedir, where
%ROOT% is automatically converted to doctrine root dir
</ul>
<li \>Doctrine::ATTR_CACHE_SLAM
<ul>
<li \> On very busy servers whenever you start the server or modify files you can create a race of many processes all trying to cache the same file at the same time. This option sets the percentage of processes that will skip trying to cache an uncached file. Or think of it as the probability of a single process to skip caching. For example, setting apc.slam_defense to 75 would mean that there is a 75% chance that the process will not cache an uncached file. So, the higher the setting the greater the defense against cache slams. Setting this to 0 disables this feature
</ul>
<li \>Doctrine::ATTR_CACHE_SIZE
<ul>
<li \> Cache size attribute
</ul>
<li \>Doctrine::ATTR_CACHE_TTL
<ul>
<li \> How often the cache is cleaned
</ul>
</ul>
* Doctrine::ATTR_CACHE_SLAM
* On very busy servers whenever you start the server or modify files you can create a race of many processes all trying to cache the same file at the same time. This option sets the percentage of processes that will skip trying to cache an uncached file. Or think of it as the probability of a single process to skip caching. For example, setting apc.slam_defense to 75 would mean that there is a 75% chance that the process will not cache an uncached file. So, the higher the setting the greater the defense against cache slams. Setting this to 0 disables this feature
* Doctrine::ATTR_CACHE_SIZE
* Cache size attribute
* Doctrine::ATTR_CACHE_TTL
* How often the cache is cleaned
Doctrine has very comprehensive and fast caching solution.
Its cache is <b>always up-to-date</b>.
Its cache is **always up-to-date**.
In order to achieve this doctrine does the following things:
<br /><br />
<table border=1 class='dashed' cellpadding=0 cellspacing=0>
<tr><td>
1. Every Doctrine_Table has its own cache directory. The default is cache/componentname/. All the cache files are saved into that directory.
|| 1. Every Doctrine_Table has its own cache directory. The default is cache/componentname/. All the cache files are saved into that directory.
The format of each cache file is [primarykey].cache.
<br /><br />
2. When retrieving records from the database doctrine always tries to hit the cache first.
<br /><br />
3. If a record (Doctrine_Record) is retrieved from database or inserted into database it will be saved into cache.
<br /><br />
4. When a Data Access Object is deleted or updated it will be deleted from the cache
</td></tr>
</table>
<br /><br />
4. When a Data Access Object is deleted or updated it will be deleted from the cache ||
Now one might wonder that this kind of solution won't work since eventually the cache will be a copy of database!
So doctrine does the following things to ensure the cache won't get too big:
<br /><br />
<table border=1 class='dashed' cellpadding=0 cellspacing=0>
<tr><td>
1. Every time a cache file is accessed the id of that record will be added into the $fetched property of Doctrine_Cache
<br /><br />
|| 1. Every time a cache file is accessed the id of that record will be added into the $fetched property of Doctrine_Cache
2. At the end of each script the Doctrine_Cache destructor will write all these primary keys at the end of a stats.cache file
<br /><br />
3. Doctrine does propabalistic cache cleaning. The default interval is 200 page loads (= 200 constructed Doctrine_Managers). Basically this means
that the average number of page loads between cache cleans is 200.
<br /><br />
4. On every cache clean stats.cache files are being read and the least accessed cache files
(cache files that have the smallest id occurance in the stats file) are then deleted.
For example if the cache size is set to 200 and the number of files in cache is 300, then 100 least accessed files are being deleted.
Doctrine also clears every stats.cache file.
Doctrine also clears every stats.cache file. ||
</td></tr>
</table>
<br /><br />
So for every 199 fast page loads there is one page load which suffers a little overhead from the cache cleaning operation.
Doctrine_Cache offers many options for performance fine-tuning:
<ul>
<li \> savePropability <br \>
* savePropability
Option that defines the propability of which
a query is getting cached.
<br \><br \>
<li \> cleanPropability <br \>
* cleanPropability
Option that defines the propability the actual cleaning will occur
when calling Doctrine_Cache::clean();
<br \><br \>
<li \> statsPropability
* statsPropability
<ul \>
<?php ?>
Doctrine_Cache offers an intuitive and easy-to-use query caching solution. It provides the following things:
<ul>
<li \> Multiple cache backends to choose from (including Memcached, APC and Sqlite)
<br \><br \>
<li \> Manual tuning and/or self-optimization. Doctrine_Cache knows how to optimize itself, yet it leaves user
* Multiple cache backends to choose from (including Memcached, APC and Sqlite)
* Manual tuning and/or self-optimization. Doctrine_Cache knows how to optimize itself, yet it leaves user
full freedom of whether or not he/she wants to take advantage of this feature.
<br \><br \>
<li \> Advanced options for fine-tuning. Doctrine_Cache has many options for fine-tuning performance.
<br \><br \>
<li \> Cache hooks itself directly into Doctrine_Db eventlistener system allowing it to be easily added on-demand.
</ul>
<br \><br \>
* Advanced options for fine-tuning. Doctrine_Cache has many options for fine-tuning performance.
* Cache hooks itself directly into Doctrine_Db eventlistener system allowing it to be easily added on-demand.
Doctrine_Cache hooks into Doctrine_Db eventlistener system allowing pluggable caching.
It evaluates queries and puts SELECT statements in cache. The caching is based on propabalistics. For example
if savePropability = 0.1 there is a 10% chance that a query gets cached.
<br \><br \>
Now eventually the cache would grow very big, hence Doctrine uses propabalistic cache cleaning.
When calling Doctrine_Cache::clean() with cleanPropability = 0.25 there is a 25% chance of the clean operation being invoked.
What the cleaning does is that it first reads all the queries in the stats file and sorts them by the number of times occurred.
Then if the size is set to 100 it means the cleaning operation will leave 100 most issued queries in cache and delete all other cache entries.
<br \><br \>
<br \><br \>
Initializing a new cache instance:
<br \><br \>
<?php
renderCode("<?php
<code type="php">
\$dbh = new Doctrine_Db('mysql:host=localhost;dbname=test', \$user, \$pass);
\$cache = new Doctrine_Cache('memcache');
......@@ -38,8 +53,11 @@ renderCode("<?php
// register it as a Doctrine_Db listener
\$dbh->addListener(\$cache);
?>");
?>
<br \><br \>
?></code>
Now you know how to set up the query cache. In the next chapter you'll learn how to tweak the cache in order to get maximum performance.
<br \><br \>
<ul>
<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:
</ul>
* Negative numbers are not permitted as indices.
* 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.
* When declaring indexed arrays with the array construct, a trailing space must be added after each comma delimiter to improve readability.
* It is also permitted to declare multiline indexed arrays using the "array" construct. In this case, each successive line must be padded with spaces.
* 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:
<code type="php">
$sampleArray = array('Doctrine', 'ORM', 1, 2, 3);
......
<ul>
<li \>Classes must be named by following the naming conventions.
</ul>
<ul>
<li \>The brace is always written right after the class name (or interface declaration).
</ul>
<ul>
<li \>Every class must have a documentation block that conforms to the PHPDocumentor standard.
</ul>
<ul>
<li \>Any code within a class must be indented four spaces.
</ul>
<ul>
<li \>Only one class is permitted per PHP file.
</ul>
<ul>
<li \>Placing additional code in a class file is NOT permitted.
</ul>
* Classes must be named by following the naming conventions.
* The brace is always written right after the class name (or interface declaration).
* Every class must have a documentation block that conforms to the PHPDocumentor standard.
* Any code within a class must be indented four spaces.
* Only one class is permitted per PHP file.
* Placing additional code in a class file is NOT permitted.
This is an example of an acceptable class declaration:
......
<?php ?>
<ul>
<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.
<code type="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:
<code type="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:
<code type="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.
<code type="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.
Documentation Format
<ul>
<li \>All documentation blocks ("docblocks") must be compatible with the phpDocumentor format. Describing the phpDocumentor format is beyond the scope of this document. For more information, visit: http://phpdoc.org/
</ul>
* All documentation blocks ("docblocks") must be compatible with the phpDocumentor format. Describing the phpDocumentor format is beyond the scope of this document. For more information, visit: http://phpdoc.org/
Methods:
<ul>
<li \>Every method, must have a docblock that contains at a minimum:
</ul>
<ul>
<li \>A description of the function
</ul>
* Every method, must have a docblock that contains at a minimum:
* A description of the function
* All of the arguments
* All of the possible return values
* It is not necessary to use the "@access" tag because the access level is already known from the "public", "private", or "protected" construct used to declare the function.
<ul>
<li \>All of the arguments
</ul>
<ul>
<li \>All of the possible return values
</ul>
* If a function/method may throw an exception, use @throws:
<ul>
<li \>It is not necessary to use the "@access" tag because the access level is already known from the "public", "private", or "protected" construct used to declare the function.
</ul>
<ul>
<li \>If a function/method may throw an exception, use @throws:
</ul>
* @throws exceptionclass [description]
<ul>
<li \>@throws exceptionclass [description]
</ul>
<ul>
<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:
</ul>
* When a string is literal (contains no variable substitutions), the apostrophe or "single quote" must always used to demarcate the string:
* 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:
* Variable substitution is permitted using the following form:
* Strings may be concatenated using the "." operator. A space must always be added before and after the "." operator to improve readability:
* 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:
<code type="php">
......
<ul>
<li \>The Doctrine ORM Framework uses the same class naming convention as PEAR and Zend framework, where the names of the classes directly
* The Doctrine ORM Framework uses the same class naming convention as PEAR and Zend framework, where the names of the classes directly
map to the directories in which they are stored. The root level directory of the Doctrine Framework is the "Doctrine/" directory,
under which all classes are stored hierarchially.
</ul>
<ul>
<li \>Class names may only contain alphanumeric characters. Numbers are permitted in class names but are discouraged.
* Class names may only contain alphanumeric characters. Numbers are permitted in class names but are discouraged.
Underscores are only permitted in place of the path separator, eg. the filename "Doctrine/Table/Exception.php" must map to the class name "Doctrine_Table_Exception".
</ul>
<ul>
<li \>If a class name is comprised of more than one word, the first letter of each new word must be capitalized. Successive capitalized letters
* If a class name is comprised of more than one word, the first letter of each new word must be capitalized. Successive capitalized letters
are not allowed, e.g. a class "XML_Reader" is not allowed while "Xml_Reader" is acceptable.
</ul>
Following rules must apply to all constants used within Doctrine framework:
<ul><li \> Constants may contain both alphanumeric characters and the underscore.</ul>
* Constants may contain both alphanumeric characters and the underscore.
<ul><li \> Constants must always have all letters capitalized.</ul>
* Constants must always have all letters capitalized.
* For readablity reasons, words in constant names must be separated by underscore characters. For example, ATTR_EXC_LOGGING is permitted but ATTR_EXCLOGGING is not.
* Constants must be defined as class members by using the "const" construct. Defining constants in the global scope with "define" is NOT permitted.
<ul><li \> For readablity reasons, words in constant names must be separated by underscore characters. For example, ATTR_EXC_LOGGING is permitted but ATTR_EXCLOGGING is not.
</ul>
<ul><li \> Constants must be defined as class members by using the "const" construct. Defining constants in the global scope with "define" is NOT permitted.
</ul>
<code type="php">
class Doctrine_SomeClass {
......
<ul>
<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.
</ul>
* For all other files, only alphanumeric characters, underscores, and the dash character ("-") are permitted. Spaces are prohibited.
* 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:
Doctrine/Db.php
Doctrine/Connection/Transaction.php
* File names must follow the mapping to class names described above.
<ul>
<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.
</ul>
<ul>
<li \>Interface classes must follow the same conventions as other classes (see above), however must end with the word "Interface"
* Interface classes must follow the same conventions as other classes (see above), however must end with the word "Interface"
(unless the interface is approved not to contain it such as Doctrine_Overloadable). Some examples:
<br \><br \>
Doctrine_Db_EventListener_Interface <br \>
<br \>
Doctrine_EventListener_Interface <br \>
</ul>
Doctrine_Db_EventListener_Interface
Doctrine_EventListener_Interface
All variables must satisfy the following conditions:
<ul>
<li \>Variable names may only contain alphanumeric characters. Underscores are not permitted. Numbers are permitted in variable names but are discouraged.
</ul>
<ul>
<li \>Variable names must always start with a lowercase letter and follow the "camelCaps" capitalization convention.
</ul>
* Variable names may only contain alphanumeric characters. Underscores are not permitted. Numbers are permitted in variable names but are discouraged.
<ul>
<li \>Verbosity is encouraged. Variables should always be as verbose as practical. Terse variable names such as "$i" and "$n" are discouraged for anything other than the smallest loop contexts. If a loop contains more than 20 lines of code, the variables for the indices need to have more descriptive names.
</ul>
<ul>
<li \>Within the framework certain generic object variables should always use the following names:
<ul>
<li \> Doctrine_Connection -> <i>$conn</i>
<li \> Doctrine_Collection -> <i>$coll</i>
<li \> Doctrine_Manager -> <i>$manager</i>
<li \> Doctrine_Query -> <i>$query</i>
<li \> Doctrine_Db -> <i>$db</i>
* Variable names must always start with a lowercase letter and follow the "camelCaps" capitalization convention.
* Verbosity is encouraged. Variables should always be as verbose as practical. Terse variable names such as "$i" and "$n" are discouraged for anything other than the smallest loop contexts. If a loop contains more than 20 lines of code, the variables for the indices need to have more descriptive names.
* Within the framework certain generic object variables should always use the following names:
* Doctrine_Connection -> //$conn//
* Doctrine_Collection -> //$coll//
* Doctrine_Manager -> //$manager//
* Doctrine_Query -> //$query//
* Doctrine_Db -> //$db//
</ul>
There are cases when more descriptive names are more appropriate (for example when multiple objects of the same class are used in same context),
in that case it is allowed to use different names than the ones mentioned.
</ul>
For files that contain only PHP code, the closing tag ("?>") is never permitted. It is not required by PHP. Not including it prevents trailing whitespace from being accidentally injected into the output.
<br \><br \>
IMPORTANT: Inclusion of arbitrary binary data as permitted by __HALT_COMPILER() is prohibited from any Doctrine framework PHP file or files derived from them. Use of this feature is only permitted for special installation scripts.
<ul>
<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).
</ul>
* 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.
* Do not use carriage returns (CR) like Macintosh computers (0x0D).
* Do not use the carriage return/linefeed combination (CRLF) as Windows computers (0x0D, 0x0A).
<b>CLASSES</b><br \><br \>
<ul>
<li \>
**CLASSES**
*
All test classes should be referring to a class or specific testing aspect of some class.
<br \><br \>
For example <i>Doctrine_Record_TestCase</i> is a valid name since its referring to class named
<i>Doctrine_Record</i>.
<br \><br \>
<i>Doctrine_Record_State_TestCase</i> is also a valid name since its referring to testing the state aspect
For example //Doctrine_Record_TestCase// is a valid name since its referring to class named
//Doctrine_Record//.
//Doctrine_Record_State_TestCase// is also a valid name since its referring to testing the state aspect
of the Doctrine_Record class.
<br \><br \>
However something like <i>Doctrine_PrimaryKey_TestCase</i> is not valid since its way too generic.
<br \><br \>
<li \> Every class should have atleast one TestCase equivalent
<li \> All testcase classes should inherit Doctrine_UnitTestCase
</ul>
<br \><br \>
<b>METHODS</b><br \><br \>
<ul>
<li \>All methods should support agile documentation; if some method failed it should be evident from the name of the test method what went wrong.
Also the test method names should give information of the system they test.<br \><br \>
For example <i>Doctrine_Export_Pgsql_TestCase::testCreateTableSupportsAutoincPks()</i> is a valid test method name. Just by looking at it we know
However something like //Doctrine_PrimaryKey_TestCase// is not valid since its way too generic.
* Every class should have atleast one TestCase equivalent
* All testcase classes should inherit Doctrine_UnitTestCase
**METHODS**
* All methods should support agile documentation; if some method failed it should be evident from the name of the test method what went wrong.
Also the test method names should give information of the system they test.
For example //Doctrine_Export_Pgsql_TestCase::testCreateTableSupportsAutoincPks()// is a valid test method name. Just by looking at it we know
what it is testing. Also we can run agile documentation tool to get little up-to-date system information.
<br \><br \>
NOTE: Commonly used testing method naming convention TestCase::test[methodName] is *NOT* allowed in Doctrine. So in this case
<b class='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
<b class='title'>Doctrine_Export_Pgsql_TestCase::testCreateTable()** would not be allowed!
* 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
divide the method into smaller methods.
</ul>
<b>ASSERTIONS</b><br \><br \>
<ul>
<li \>There should never be assertions within any loops and rarely within functions.
**ASSERTIONS**
* There should never be assertions within any loops and rarely within functions.
......@@ -3,19 +3,21 @@ If the same attribute is set on both lower level and upper level, the uppermost
if user first sets default fetchmode in global level to Doctrine::FETCH_BATCH and then sets 'example' table fetchmode to Doctrine::FETCH_LAZY,
the lazy fetching strategy will be used whenever the records of 'example' table are being fetched.
<br \><br \>
<li> Global level
<ul>
The attributes set in global level will affect every connection and every table in each connection.
</ul>
<li> Connection level
<ul>
The attributes set in connection level will take effect on each table in that connection.
</ul>
<li> Table level
<ul>
The attributes set in table level will take effect only on that table.
</ul>
<code type="php">
// setting a global level attribute
......
......@@ -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
<code type="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 \>
<div class='sql'>SELECT * FROM `sometable` WHERE `id` = '123'</div>
<br \>
as opposed to:
<br \>
<div class='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
mysql, mysqli not-null constraints Doctrine::ERROR_CONSTRAINT Doctrine::ERROR_CONSTRAINT_NOT_NULL
<br \><br \>
<i>Doctrine::PORTABILITY_FIX_ASSOC_FIELD_NAMES</i>
<br \><br \>
//Doctrine::PORTABILITY_FIX_ASSOC_FIELD_NAMES//
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
<br \><br \>
<?php
renderCode("<?php
<code type="php">
\$conn->setAttribute('portability',
Doctrine::PORTABILITY_FIX_CASE | Doctrine::PORTABILITY_RTRIM);
?>");
?>
?></code>
<br \><br \>
Using setAttribute() to enable all portability options except trimming
<br \><br \>
<?php
renderCode("<?php
<code type="php">
\$conn->setAttribute('portability',
Doctrine::PORTABILITY_ALL ^ Doctrine::PORTABILITY_RTRIM);
?>");
?>
?></code>
<?php ?>
Doctrine allows you to bind connections to components (= your ActiveRecord classes). This means everytime a component issues a query
or data is being fetched from the table the component is pointing at Doctrine will use the bound connection.
<br \> <br \>
<?php
renderCode("<?php
<code type="php">
\$conn = \$manager->openConnection(new PDO('dsn','username','password'), 'connection 1');
\$conn2 = \$manager->openConnection(new PDO('dsn2','username2','password2'), 'connection 2');
......@@ -19,6 +20,5 @@ renderCode("<?php
// Doctrine uses 'connection 2' for fetching here
\$groups = \$q->from('Group g')->where('g.id IN (1,2,3)')->execute();
?>");
?>
?></code>
<?php ?>
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
<code type="php">
// we may use PDO / PEAR like DSN
// here we use PEAR like DSN
\$dbh = new Doctrine_Db('mysql://username:password@localhost/test');
......@@ -17,6 +20,5 @@ renderCode("<?php
// connects database and performs a query
\$conn->query('FROM User u');
?>");
?>
?></code>
......@@ -2,9 +2,10 @@
From the start Doctrine has been designed to work with multiple connections. Unless separately specified Doctrine always uses the current connection
for executing the queries. The following example uses openConnection() second argument as an optional
connection alias.
<br \><br \>
<?php
renderCode("<?php
<code type="php">
// Doctrine_Manager controls all the connections
\$manager = Doctrine_Manager::getInstance();
......@@ -13,14 +14,16 @@ renderCode("<?php
\$conn = \$manager->openConnection(new PDO('dsn','username','password'), 'connection 1');
?>
");
?>
<br \><br \>
</code>
For convenience Doctrine_Manager provides static method connection() which opens new connection when arguments are given to it and returns the current
connection when no arguments have been speficied.
<br \><br \>
<?php
renderCode("<?php
<code type="php">
// open first connection
\$conn = Doctrine_Manager::connection(new PDO('dsn','username','password'), 'connection 1');
......@@ -29,44 +32,49 @@ renderCode("<?php
// \$conn2 == \$conn
?>
");
?>
</code>
<br \><br \>
The current connection is the lastly opened connection.
<br \><br \>
<?php
renderCode("<?php
<code type="php">
// open second connection
\$conn2 = \$manager->openConnection(new PDO('dsn2','username2','password2'), 'connection 2');
\$manager->getCurrentConnection(); // \$conn2
?>");
?>
<br \><br \>
?></code>
You can change the current connection by calling setCurrentConnection().
<br \><br \>
<?php
renderCode("<?php
<code type="php">
\$manager->setCurrentConnection('connection 1');
\$manager->getCurrentConnection(); // \$conn
?>
");
?>
<br \><br \>
</code>
You can iterate over the opened connection by simple passing the manager object to foreach clause. This is possible since Doctrine_Manager implements
special IteratorAggregate interface.
<br \><br \>
<?php
renderCode("<?php
<code type="php">
// iterating through connections
foreach(\$manager as \$conn) {
}
?>");
?>
?></code>
<?php ?>
Opening a new database connection in Doctrine is very easy. If you wish to use PDO (www.php.net/PDO) you can just initalize a new PDO object:
<br \> <br \>
<?php
renderCode("<?php
<code type="php">
\$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
\$user = 'dbuser';
\$password = 'dbpass';
......@@ -12,13 +13,15 @@ try {
} catch (PDOException \$e) {
echo 'Connection failed: ' . \$e->getMessage();
}
?>");
?>
<br \><br \>
?></code>
If your database extension isn't supported by PDO you can use special Doctrine_Adapter class (if availible). The following example uses db2 adapter:
<br \><br \>
<?php
renderCode("<?php
<code type="php">
\$dsn = 'db2:dbname=testdb;host=127.0.0.1';
\$user = 'dbuser';
\$password = 'dbpass';
......@@ -28,14 +31,15 @@ try {
} catch (PDOException \$e) {
echo 'Connection failed: ' . \$e->getMessage();
}
?>");
?>
<br \><br \>
?></code>
The next step is opening a new Doctrine_Connection.
<br \><br \>
<?php
renderCode("<?php
<code type="php">
\$conn = Doctrine_Manager::connection(\$dbh);
?>");
?>
?></code>
<?php ?>
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
<code type="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.
......
Syntax:
<div class='sql'>
<pre>
<code>
operand comparison_operator ANY (subquery)
operand comparison_operator SOME (subquery)
operand comparison_operator ALL (subquery)
</pre>
</div>
</code>
An ALL conditional expression returns true if the comparison operation is true for all values
in the result of the subquery or the result of the subquery is empty. An ALL conditional expression
is false if the result of the comparison is false for at least one row, and is unknown if neither true nor
false.
<br \><br \>
<div class='sql'>
<pre>
<code>
FROM C WHERE C.col1 < ALL (FROM C2(col1))
</pre>
</div>
</code>
An ANY conditional expression returns true if the comparison operation is true for some
value in the result of the subquery. An ANY conditional expression is false if the result of the subquery
is empty or if the comparison operation is false for every value in the result of the subquery, and is
unknown if neither true nor false.
<div class='sql'>
<pre>
<code>
FROM C WHERE C.col1 > ANY (FROM C2(col1))
</pre>
</div>
</code>
The keyword SOME is an alias for ANY.
<div class='sql'>
<pre>
<code>
FROM C WHERE C.col1 > SOME (FROM C2(col1))
</pre>
</div>
<br \>
</code>
The comparison operators that can be used with ALL or ANY conditional expressions are =, <, <=, >, >=, <>. The
result of the subquery must be same type with the conditional expression.
<br \><br \>
NOT IN is an alias for <> ALL. Thus, these two statements are equal:
<br \><br \>
<div class='sql'>
<pre>
<code>
FROM C WHERE C.col1 <> ALL (FROM C2(col1));
FROM C WHERE C.col1 NOT IN (FROM C2(col1));
</pre>
</div>
</code>
Syntax:
<div class='sql'>
<pre>
<i>operand</i> [NOT ]EXISTS (<i>subquery</i>)
</pre>
</div>
The EXISTS operator returns TRUE if the subquery returns one or more rows and FALSE otherwise. <br \>
<br \>
The NOT EXISTS operator returns TRUE if the subquery returns 0 rows and FALSE otherwise.<br \>
<br \>
<code>
//operand// [NOT ]EXISTS (//subquery//)
</code>
The EXISTS operator returns TRUE if the subquery returns one or more rows and FALSE otherwise.
The NOT EXISTS operator returns TRUE if the subquery returns 0 rows and FALSE otherwise.
Finding all articles which have readers:
<div class='sql'>
<pre>
<code>
FROM Article
WHERE EXISTS (FROM ReaderLog(id)
WHERE ReaderLog.article_id = Article.id)
</pre>
</div>
</code>
Finding all articles which don't have readers:
<div class='sql'>
<pre>
<code>
FROM Article
WHERE NOT EXISTS (FROM ReaderLog(id)
WHERE ReaderLog.article_id = Article.id)
</pre>
</div>
</code>
Syntax:
<div class='sql'>
<pre>
<i>operand</i> IN (<i>subquery</i>|<i>value list</i>)
</pre>
</div>
An IN conditional expression returns true if the <i>operand</i> is found from result of the <i>subquery</i>
or if its in the specificied comma separated <i>value list</i>, hence the IN expression is always false if the result of the subquery
<code>
//operand// IN (//subquery//|//value list//)
</code>
An IN conditional expression returns true if the //operand// is found from result of the //subquery//
or if its in the specificied comma separated //value list//, hence the IN expression is always false if the result of the subquery
is empty.
When <i>value list</i> is being used there must be at least one element in that list.
When //value list// is being used there must be at least one element in that list.
<div class='sql'>
<pre>
<code>
FROM C1 WHERE C1.col1 IN (FROM C2(col1));
FROM User WHERE User.id IN (1,3,4,5)
</pre>
</div>
</code>
The keyword IN is an alias for = ANY. Thus, these two statements are equal:
<div class='sql'>
<pre>
<code>
FROM C1 WHERE C1.col1 = ANY (FROM C2(col1));
FROM C1 WHERE C1.col1 IN (FROM C2(col1));
</pre>
</div>
</code>
Syntax:<br \>
Syntax:
string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]
<br \>
<br \>
The string_expression must have a string value. The pattern_value is a string literal or a string-valued
input parameter in which an underscore (_) stands for any single character, a percent (%) character
stands for any sequence of characters (including the empty sequence), and all other characters stand for
themselves. The optional escape_character is a single-character string literal or a character-valued
input parameter (i.e., char or Character) and is used to escape the special meaning of the underscore
and percent characters in pattern_value.
<br \><br \>
Examples:
<br \>
<ul>
<li \>address.phone LIKE 12%3 is true for '123' '12993' and false for '1234'
<li \>asentence.word LIKE l_se is true for lose and false for 'loose'
<li \>aword.underscored LIKE \_% ESCAPE '\' is true for '_foo' and false for 'bar'
<li \>address.phone NOT LIKE ‘12%3’ is false for '123' and '12993' and true for '1234'
</ul>
<br \>
* address.phone LIKE 12%3 is true for '123' '12993' and false for '1234'
* asentence.word LIKE l_se is true for lose and false for 'loose'
* aword.underscored LIKE \_% ESCAPE '\' is true for '_foo' and false for 'bar'
* address.phone NOT LIKE ‘12%3’ is false for '123' and '12993' and true for '1234'
If the value of the string_expression or pattern_value is NULL or unknown, the value of the LIKE
expression is unknown. If the escape_characteris specified and is NULL, the value of the LIKE expression
is unknown.
......
<b>Strings</b><br \>
**Strings**
A string literal is enclosed in single quotesfor example: 'literal'. A string literal that includes a single
quote is represented by two single quotesfor example: 'literal''s'.
<div class='sql'>
<pre>
<code>
FROM User WHERE User.name = 'Vincent'
</pre>
</div>
</code>
**Integers**
<b>Integers</b><br \>
Integer literals support the use of PHP integer literal syntax.
<div class='sql'>
<pre>
<code>
FROM User WHERE User.id = 4
</pre>
</div>
</code>
**Floats**
<b>Floats</b><br \>
Float literals support the use of PHP float literal syntax.
<div class='sql'>
<pre>
<code>
FROM Account WHERE Account.amount = 432.123
</pre>
</div>
</code>
**Booleans**
<br \>
<b>Booleans</b><br \>
The boolean literals are true and false.
<div class='sql'>
<pre>
<code>
FROM User WHERE User.admin = true
FROM Session WHERE Session.is_authed = false
</pre>
</div>
</code>
**Enums**
<br \>
<b>Enums</b><br \>
The enumerated values work in the same way as string literals.
<div class='sql'>
<pre>
<code>
FROM User WHERE User.type = 'admin'
</pre>
</div>
</code>
<br \>
Predefined reserved literals are case insensitive, although its a good standard to write them in uppercase.
The operators are listed below in order of decreasing precedence.
<ul>
<li \> Navigation operator (.)
<li \> Arithmetic operators: <br \>
+, - unary <br \>
*, / multiplication and division <br \>
+, - addition and subtraction <br \>
<li \> Comparison operators : =, >, >=, <, <=, <> (not equal), [NOT] LIKE, <br \>
[NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY <br \>
<li \> Logical operators:<br \>
NOT <br \>
AND <br \>
OR <br \>
</ul>
* Navigation operator (.)
* Arithmetic operators:
+, - unary
*, / multiplication and division
+, - addition and subtraction
* Comparison operators : =, >, >=, <, <=, <> (not equal), [NOT] LIKE,
[NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY
* Logical operators:
NOT
AND
OR
A subquery can contain any of the keywords or clauses that an ordinary SELECT query can contain.
<br \><br \>
Some advantages of the subqueries:
<ul>
<li \>They allow queries that are structured so that it is possible to isolate each part of a statement.
<li \>They provide alternative ways to perform operations that would otherwise require complex joins and unions.
* They allow queries that are structured so that it is possible to isolate each part of a statement.
* They provide alternative ways to perform operations that would otherwise require complex joins and unions.
* They are, in many people'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 \>They are, in many people's opinion, readable. Indeed, it was the innovation of subqueries that gave people the original idea of calling the early SQL “Structured Query Language.”
</ul>
<code type="php">
// finding all users which don't belong to any group 1
......
<div class='sql'>
<pre>
DELETE FROM <i>component_name</i>
[WHERE <i>where_condition</i>]
<code>
DELETE FROM //component_name//
[WHERE //where_condition//]
[ORDER BY ...]
[LIMIT <i>record_count</i>]
</pre>
</div>
<ul>
<li \>The DELETE statement deletes records from <i>component_name</i> and returns the number of records deleted.
[LIMIT //record_count//]
</code>
* The DELETE statement deletes records from //component_name// and returns the number of records deleted.
<li \>The optional WHERE clause specifies the conditions that identify which records to delete.
* The optional WHERE clause specifies the conditions that identify which records to delete.
Without WHERE clause, all records are deleted.
<li \>If the ORDER BY clause is specified, the records are deleted in the order that is specified.
* If the ORDER BY clause is specified, the records are deleted in the order that is specified.
* The LIMIT clause places a limit on the number of rows that can be deleted.
The statement will stop as soon as it has deleted //record_count// records.
<li \>The LIMIT clause places a limit on the number of rows that can be deleted.
The statement will stop as soon as it has deleted <i>record_count</i> records.
</ul>
<code type="php">
$q = 'DELETE FROM Account WHERE id > ?';
......
Syntax: <br \>
Syntax:
<div class='sql'>
<pre>
FROM <i>component_reference</i> [[LEFT | INNER] JOIN <i>component_reference</i>] ...
</pre>
</div>
<code>
FROM //component_reference// [[LEFT | INNER] JOIN //component_reference//] ...
</code>
The FROM clause indicates the component or components from which to retrieve records.
If you name more than one component, you are performing a join.
For each table specified, you can optionally specify an alias.
<br \><br \>
<li \> The default join type is <i>LEFT JOIN</i>. This join can be indicated by the use of either 'LEFT JOIN' clause or simply ',', hence the following queries are equal:
<div class='sql'>
<pre>
* The default join type is //LEFT JOIN//. This join can be indicated by the use of either 'LEFT JOIN' clause or simply ',', hence the following queries are equal:
<code>
SELECT u.*, p.* FROM User u LEFT JOIN u.Phonenumber
SELECT u.*, p.* FROM User u, u.Phonenumber p
</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).
So basically //INNER JOIN// can be used when you want to efficiently fetch for example all users which have one or more phonenumbers.
<code>
SELECT u.*, p.* FROM User u INNER JOIN u.Phonenumber p
</pre>
</div>
</code>
<?php ?>
<ul>
<li \>The <i>CONCAT</i> function returns a string that is a concatenation of its arguments. In the example above we
* The //CONCAT// function returns a string that is a concatenation of its arguments. In the example above we
map the concatenation of users firstname and lastname to a value called name
<br \><br \><?php
renderCode("<?php
<code type="php">
\$q = new Doctrine_Query();
\$users = \$q->select('CONCAT(u.firstname, u.lastname) name')->from('User u')->execute();
......@@ -13,15 +14,17 @@ foreach(\$users as \$user) {
// its a mapped function value
print \$user->name;
}
?>");
?>
?></code>
<br \><br \>
<li \>The second and third arguments of the <i>SUBSTRING</i> function denote the starting position and length of
* The second and third arguments of the //SUBSTRING// function denote the starting position and length of
the substring to be returned. These arguments are integers. The first position of a string is denoted by 1.
The <i>SUBSTRING</i> function returns a string.
<br \><br \><?php
renderCode("<?php
The //SUBSTRING// function returns a string.
<code type="php">
\$q = new Doctrine_Query();
\$users = \$q->select('u.name')->from('User u')->where(\"SUBSTRING(u.name, 0, 1) = 'z'\")->execute();
......@@ -29,16 +32,18 @@ renderCode("<?php
foreach(\$users as \$user) {
print \$user->name;
}
?>");
?>
<br \><br \>
?></code>
<li \>The <i>TRIM</i> function trims the specified character from a string. If the character to be trimmed is not
* The //TRIM// function trims the specified character from a string. If the character to be trimmed is not
specified, it is assumed to be space (or blank). The optional trim_character is a single-character string
literal or a character-valued input parameter (i.e., char or Character)[30]. If a trim specification is
not provided, BOTH is assumed. The <i>TRIM</i> function returns the trimmed string.
<br \><br \><?php
renderCode("<?php
not provided, BOTH is assumed. The //TRIM// function returns the trimmed string.
<code type="php">
\$q = new Doctrine_Query();
\$users = \$q->select('u.name')->from('User u')->where(\"TRIM(u.name) = 'Someone'\")->execute();
......@@ -46,13 +51,15 @@ renderCode("<?php
foreach(\$users as \$user) {
print \$user->name;
}
?>");
?> <br \><br \>
<li \>The <i>LOWER</i> and <i>UPPER</i> functions convert a string to lower and upper case, respectively. They return a
string. <br \><br \>
?></code>
* The //LOWER// and //UPPER// functions convert a string to lower and upper case, respectively. They return a
string.
<?php
renderCode("<?php
<code type="php">
\$q = new Doctrine_Query();
\$users = \$q->select('u.name')->from('User u')->where(\"LOWER(u.name) = 'someone'\")->execute();
......@@ -60,18 +67,21 @@ renderCode("<?php
foreach(\$users as \$user) {
print \$user->name;
}
?>");
?> <br \><br \>
<li \>
The <i>LOCATE</i> function returns the position of a given string within a string, starting the search at a specified
?></code>
*
The //LOCATE// function returns the position of a given string within a string, starting the search at a specified
position. It returns the first position at which the string was found as an integer. The first argument
is the string to be located; the second argument is the string to be searched; the optional third argument
is an integer that represents the string position at which the search is started (by default, the beginning of
the string to be searched). The first position in a string is denoted by 1. If the string is not found, 0 is
returned.
<br \><br \>
<li \>The <i>LENGTH</i> function returns the length of the string in characters as an integer.
</ul>
* The //LENGTH// function returns the length of the string in characters as an integer.
<ul>
<li> GROUP BY and HAVING clauses can be used for dealing with aggregate functions
<br \>
<li> Following aggregate functions are availible on DQL: COUNT, MAX, MIN, AVG, SUM
<br \>
Selecting alphabetically first user by name.
<div class='sql'>
<pre>
<code>
SELECT MIN(u.name) FROM User u
</pre>
</div>
</code>
Selecting the sum of all Account amounts.
<div class='sql'>
<pre>
<code>
SELECT SUM(a.amount) FROM Account a
</pre>
</div>
</code>
<br \>
<li> Using an aggregate function in a statement containing no GROUP BY clause, results in grouping on all rows. In the example above
we fetch all users and the number of phonenumbers they have.
<div class='sql'>
<pre>
<code>
SELECT u.*, COUNT(p.id) FROM User u, u.Phonenumber p GROUP BY u.id
</pre>
</div>
</code>
<br \>
<li> The HAVING clause can be used for narrowing the results using aggregate values. In the following example we fetch
all users which have atleast 2 phonenumbers
<div class='sql'>
<pre>
<code>
SELECT u.* FROM User u, u.Phonenumber p HAVING COUNT(p.id) >= 2
</pre>
</div>
</code>
</ul>
<code type="php">
......
Doctrine Query Language(DQL) is an Object Query Language created for helping users in complex object retrieval.
You should always consider using DQL(or raw SQL) when retrieving relational data efficiently (eg. when fetching users and their phonenumbers).
<br \><br \>
When compared to using raw SQL, DQL has several benefits: <br \>
<ul>
<li \>From the start it has been designed to retrieve records(objects) not result set rows
</ul>
<ul>
<li \>DQL understands relations so you don't have to type manually sql joins and join conditions
</ul>
<ul>
<li \>DQL is portable on different databases
</ul>
<ul>
<li \>DQL has some very complex built-in algorithms like (the record limit algorithm) which can help
When compared to using raw SQL, DQL has several benefits:
* From the start it has been designed to retrieve records(objects) not result set rows
* DQL understands relations so you don't have to type manually sql joins and join conditions
* DQL is portable on different databases
* DQL has some very complex built-in algorithms like (the record limit algorithm) which can help
developer to efficiently retrieve objects
</ul>
<ul>
<li \>It supports some functions that can save time when dealing with one-to-many, many-to-many relational data with conditional fetching.
</ul>
* It supports some functions that can save time when dealing with one-to-many, many-to-many relational data with conditional fetching.
If the power of DQL isn't enough, you should consider using the rawSql API for object population.
......@@ -29,9 +32,11 @@ If the power of DQL isn't enough, you should consider using the rawSql API for o
$users = $conn->getTable('User')->findAll();
foreach($users as $user) {
print $user->name."<br \>";
print $user->name."
";
foreach($user->Phonenumber as $phonenumber) {
print $phonenumber."<br \>";
print $phonenumber."
";
}
}
......@@ -41,9 +46,11 @@ foreach($users as $user) {
$users = $conn->query("FROM User.Phonenumber");
foreach($users as $user) {
print $user->name."<br \>";
print $user->name."
";
foreach($user->Phonenumber as $phonenumber) {
print $phonenumber."<br \>";
print $phonenumber."
";
}
}
......
DQL LIMIT clause is portable on all supported databases. Special attention have been paid to following facts:
<br \><br \>
<li \> Only Mysql, Pgsql and Sqlite implement LIMIT / OFFSET clauses natively
<br \><br \>
<li \> In Oracle / Mssql / Firebird LIMIT / OFFSET clauses need to be emulated in driver specific way
<br \><br \>
<li \> The limit-subquery-algorithm needs to execute to subquery separately in mysql, since mysql doesn't yet support
LIMIT clause in subqueries<br \><br \>
<li \> Pgsql needs the order by fields to be preserved in SELECT clause, hence LS-algorithm needs to take this into consideration
when pgsql driver is used<br \><br \>
<li \> Oracle only allows < 30 object identifiers (= table/column names/aliases), hence the limit subquery must use as short aliases as possible
* Only Mysql, Pgsql and Sqlite implement LIMIT / OFFSET clauses natively
* In Oracle / Mssql / Firebird LIMIT / OFFSET clauses need to be emulated in driver specific way
* The limit-subquery-algorithm needs to execute to subquery separately in mysql, since mysql doesn't yet support
LIMIT clause in subqueries
* Pgsql needs the order by fields to be preserved in SELECT clause, hence LS-algorithm needs to take this into consideration
when pgsql driver is used
* Oracle only allows < 30 object identifiers (= table/column names/aliases), hence the limit subquery must use as short aliases as possible
and it must avoid alias collisions with the main query.
The limit-subquery-algorithm is an algorithm that DQL parser uses internally when one-to-many / many-to-many relational
data is being fetched simultaneously. This kind of special algorithm is needed for the LIMIT clause to limit the number
of records instead of sql result set rows.
<br \><br \>
In the following example we have users and phonenumbers with their relation being one-to-many. Now lets say we want fetch the first 20 users
and all their related phonenumbers.
<br \><br \>
Now one might consider that adding a simple driver specific LIMIT 20 at the end of query would return the correct results.
Thats wrong, since we you might get anything between 1-20 users as the first user might have 20 phonenumbers and then record set would consist of 20 rows.
<br \><br \>
DQL overcomes this problem with subqueries and with complex but efficient subquery analysis. In the next example we are going to fetch first 20 users and all their phonenumbers with single efficient query.
Notice how the DQL parser is smart enough to use column aggregation inheritance even in the subquery and how its smart enough to use different aliases
for the tables in the subquery to avoid alias collisions.
<br \><br \>
DQL QUERY:
<div class='sql'><pre>
SELECT u.id, u.name, p.* FROM User u LEFT JOIN u.Phonenumber p LIMIT 20
</pre></div>
SQL QUERY:
<div class='sql'>
<pre>
<code>
SELECT
e.id AS e__id,
e.name AS e__name,
......@@ -33,23 +40,25 @@ WHERE e.id IN (
SELECT DISTINCT e2.id
FROM entity e2
WHERE (e2.type = 0) LIMIT 20) AND (e.type = 0)
</pre>
</div>
</code>
<br \><br \>
In the next example we are going to fetch first 20 users and all their phonenumbers and only
those users that actually have phonenumbers with single efficient query, hence we use an INNER JOIN.
Notice how the DQL parser is smart enough to use the INNER JOIN in the subquery.
<br \><br \>
DQL QUERY:
<div class='sql'><pre>
SELECT u.id, u.name, p.* FROM User u LEFT JOIN u.Phonenumber p LIMIT 20
</pre></div>
SQL QUERY:
<div class='sql'>
<pre>
<code>
SELECT
e.id AS e__id,
e.name AS e__name,
......@@ -63,7 +72,6 @@ SELECT DISTINCT e2.id
FROM entity e2
INNER JOIN phonenumber p2 ON e2.id = p2.entity_id
WHERE (e2.type = 0) LIMIT 20) AND (e.type = 0)
</pre>
</div>
</code>
Record collections can be sorted efficiently at the database level using the ORDER BY clause.
Syntax:
<div class='sql'>
<pre>
<code>
[ORDER BY {ComponentAlias.columnName}
[ASC | DESC], ...]
</pre>
</div>
</code>
Examples:
<br \>
<div class='sql'>
<pre>
<code>
FROM User.Phonenumber
ORDER BY User.name, Phonenumber.phonenumber
FROM User u, u.Email e
ORDER BY e.address, u.id
</pre>
</div>
</code>
In order to sort in reverse order you can add the DESC (descending) keyword to the name of the column in the ORDER BY clause that you are sorting by. The default is ascending order; this can be specified explicitly using the ASC keyword.
<br \>
<div class='sql'>
<pre>
<code>
FROM User u, u.Email e
ORDER BY e.address DESC, u.id ASC;
</pre>
</div>
</code>
<li>
<p>NOT, !
</p>
<p>
NOT, !
Logical NOT. Evaluates to 1 if the
operand is 0, to 0 if
the operand is non-zero, and NOT NULL
returns NULL.
</p>
<b class='title'>DQL condition :</b> NOT 10<br \>
-&gt; 0<br \>
<b class='title'>DQL condition :</b> NOT 0<br \>
-&gt; 1<br \>
<b class='title'>DQL condition :</b> NOT NULL<br \>
-&gt; NULL<br \>
<b class='title'>DQL condition :</b> ! (1+1)<br \>
-&gt; 0<br \>
<b class='title'>DQL condition :</b> ! 1+1<br \>
-&gt; 1<br \>
<b class='title'>DQL condition :** NOT 10
-&gt; 0
<b class='title'>DQL condition :** NOT 0
-&gt; 1
<b class='title'>DQL condition :** NOT NULL
-&gt; NULL
<b class='title'>DQL condition :** ! (1+1)
-&gt; 0
<b class='title'>DQL condition :** ! 1+1
-&gt; 1
</pre>
<p>
The last example produces 1 because the
expression evaluates the same way as
(!1)+1.
</p>
</li>
<li>
<p><a name="function_and"></a>
<a name="function_and"></a>
<a class="indexterm" name="id2965271"></a>
<a class="indexterm" name="id2965283"></a>
AND
</p>
<p>
Logical AND. Evaluates to 1 if all
operands are non-zero and not NULL, to
0 if one or more operands are
0, otherwise NULL is
returned.
</p>
<b class='title'>DQL condition :</b> 1 AND 1<br \>
-&gt; 1<br \>
<b class='title'>DQL condition :</b> 1 AND 0<br \>
-&gt; 0<br \>
<b class='title'>DQL condition :</b> 1 AND NULL<br \>
-&gt; NULL<br \>
<b class='title'>DQL condition :</b> 0 AND NULL<br \>
-&gt; 0<br \>
<b class='title'>DQL condition :</b> NULL AND 0<br \>
-&gt; 0<br \>
<b class='title'>DQL condition :** 1 AND 1
-&gt; 1
<b class='title'>DQL condition :** 1 AND 0
-&gt; 0
<b class='title'>DQL condition :** 1 AND NULL
-&gt; NULL
<b class='title'>DQL condition :** 0 AND NULL
-&gt; 0
<b class='title'>DQL condition :** NULL AND 0
-&gt; 0
</pre>
</li>
<li>
OR
</p>
<p>
Logical OR. When both operands are
non-NULL, the result is
1 if any operand is non-zero, and
......@@ -67,45 +99,69 @@
NULL otherwise. If both operands are
NULL, the result is
NULL.
</p>
<b class='title'>DQL condition :</b> 1 OR 1<br \>
-&gt; 1<br \>
<b class='title'>DQL condition :</b> 1 OR 0<br \>
-&gt; 1<br \>
<b class='title'>DQL condition :</b> 0 OR 0<br \>
-&gt; 0<br \>
<b class='title'>DQL condition :</b> 0 OR NULL<br \>
-&gt; NULL<br \>
<b class='title'>DQL condition :</b> 1 OR NULL<br \>
-&gt; 1<br \>
<b class='title'>DQL condition :** 1 OR 1
-&gt; 1
<b class='title'>DQL condition :** 1 OR 0
-&gt; 1
<b class='title'>DQL condition :** 0 OR 0
-&gt; 0
<b class='title'>DQL condition :** 0 OR NULL
-&gt; NULL
<b class='title'>DQL condition :** 1 OR NULL
-&gt; 1
</pre>
</li>
<li>
<p><a name="function_xor"></a>
<a name="function_xor"></a>
<a class="indexterm" name="id2965520"></a>
XOR
</p>
<p>
Logical XOR. Returns NULL if either
operand is NULL. For
non-NULL operands, evaluates to
1 if an odd number of operands is
non-zero, otherwise 0 is returned.
</p>
<b class='title'>DQL condition :</b> 1 XOR 1<br \>
-&gt; 0<br \>
<b class='title'>DQL condition :</b> 1 XOR 0<br \>
-&gt; 1<br \>
<b class='title'>DQL condition :</b> 1 XOR NULL<br \>
-&gt; NULL<br \>
<b class='title'>DQL condition :</b> 1 XOR 1 XOR 1<br \>
-&gt; 1<br \>
<b class='title'>DQL condition :** 1 XOR 1
-&gt; 0
<b class='title'>DQL condition :** 1 XOR 0
-&gt; 1
<b class='title'>DQL condition :** 1 XOR NULL
-&gt; NULL
<b class='title'>DQL condition :** 1 XOR 1 XOR 1
-&gt; 1
</pre>
<p>
a XOR b is mathematically equal to
(a AND (NOT b)) OR ((NOT a) and b).
</p>
</li>
</ul>
SELECT statement syntax:
<div class='sql'>
<pre>
<code>
SELECT
[ALL | DISTINCT]
<i>select_expr</i>, ...
[FROM <i>components</i>
[WHERE <i>where_condition</i>]
[GROUP BY <i>groupby_expr</i>
//select_expr//, ...
[FROM //components//
[WHERE //where_condition//]
[GROUP BY //groupby_expr//
[ASC | DESC], ... ]
[HAVING <i>where_condition</i>]
[ORDER BY <i>orderby_expr</i>
[HAVING //where_condition//]
[ORDER BY //orderby_expr//
[ASC | DESC], ...]
[LIMIT <i>row_count</i> OFFSET <i>offset</i>}]
</pre>
</div>
<br \>
[LIMIT //row_count// OFFSET //offset//}]
</code>
The SELECT statement is used for the retrieval of data from one or more components.
<ul>
<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>
SELECT a.name, a.amount FROM Account a
</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
* 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
(Doctrine converts asterisk to appropriate column names, hence leading to better performance on some databases).
<div class='sql'>
<pre>
<code>
SELECT a.* FROM Account a
</pre>
</div>
<li \>FROM clause <i>components</i> indicates the component or components from which to retrieve records.
<div class='sql'>
<pre>
</code>
* FROM clause //components// indicates the component or components from which to retrieve records.
<code>
SELECT a.* FROM Account a
SELECT u.*, p.*, g.* FROM User u LEFT JOIN u.Phonenumber p LEFT JOIN u.Group g
</pre>
</div>
<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>
* The WHERE clause, if given, indicates the condition or conditions that the records must satisfy to be selected. //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>
SELECT a.* FROM Account a WHERE a.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
</code>
* In the WHERE clause, you can use any of the functions and operators that DQL supports, except for aggregate (summary) functions
<li \>The HAVING clause can be used for narrowing the results with aggregate functions
<div class='sql'>
<pre>
* The HAVING clause can be used for narrowing the results with aggregate functions
<code>
SELECT u.* FROM User u LEFT JOIN u.Phonenumber p HAVING COUNT(p.id) > 3
</pre>
</div>
<li \>The ORDER BY clause can be used for sorting the results
<div class='sql'>
<pre>
</code>
* The ORDER BY clause can be used for sorting the results
<code>
SELECT u.* FROM User u ORDER BY u.name
</pre>
</div>
<li \>The LIMIT and OFFSET clauses can be used for efficiently limiting the number of records to a given <i>row_count</i>
<div class='sql'>
<pre>
</code>
* The LIMIT and OFFSET clauses can be used for efficiently limiting the number of records to a given //row_count//
<code>
SELECT u.* FROM User u LIMIT 20
</pre>
</div>
</ul>
</code>
UPDATE statement syntax:
<div class='sql'>
<pre>
UPDATE <i>component_name</i>
SET <i>col_name1</i>=<i>expr1</i> [, <i>col_name2</i>=<i>expr2</i> ...]
[WHERE <i>where_condition</i>]
<code>
UPDATE //component_name//
SET //col_name1//=//expr1// [, //col_name2//=//expr2// ...]
[WHERE //where_condition//]
[ORDER BY ...]
[LIMIT <i>record_count</i>]
</pre>
</div>
<ul>
<li \>The UPDATE statement updates columns of existing records in <i>component_name</i> with new values and returns the number of affected records.
[LIMIT //record_count//]
</code>
* The UPDATE statement updates columns of existing records in //component_name// with new values and returns the number of affected records.
<li \>The SET clause indicates which columns to modify and the values they should be given.
* The SET clause indicates which columns to modify and the values they should be given.
<li \>The optional WHERE clause specifies the conditions that identify which records to update.
* The optional WHERE clause specifies the conditions that identify which records to update.
Without WHERE clause, all records are updated.
<li \>The optional ORDER BY clause specifies the order in which the records are being updated.
* The optional ORDER BY clause specifies the order in which the records are being updated.
* The LIMIT clause places a limit on the number of records that can be updated. You can use LIMIT row_count to restrict the scope of the UPDATE.
A LIMIT clause is a **rows-matched restriction** not a rows-changed restriction.
The statement stops as soon as it has found //record_count// rows that satisfy the WHERE clause, whether or not they actually were changed.
<li \>The LIMIT clause places a limit on the number of records that can be updated. You can use LIMIT row_count to restrict the scope of the UPDATE.
A LIMIT clause is a <b>rows-matched restriction</b> not a rows-changed restriction.
The statement stops as soon as it has found <i>record_count</i> rows that satisfy the WHERE clause, whether or not they actually were changed.
</ul>
<code type="php">
$q = 'UPDATE Account SET amount = amount + 200 WHERE id > 200';
......
Syntax:
<div class='sql'>
<pre>
WHERE <i>where_condition</i>
</pre>
</div>
<ul>
<li \> The WHERE clause, if given, indicates the condition or conditions that the records must satisfy to be selected.
<br \><br \>
<li \> <i>where_condition</i> is an expression that evaluates to true for each row to be selected.
<br \><br \>
<li \> The statement selects all rows if there is no WHERE clause.
<br \><br \>
<li \> When narrowing results with aggregate function values HAVING clause should be used instead of WHERE clause
<br \><br \>
</ul>
<code>
WHERE //where_condition//
</code>
* The WHERE clause, if given, indicates the condition or conditions that the records must satisfy to be selected.
* //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.
* When narrowing results with aggregate function values HAVING clause should be used instead of WHERE clause
Following attributes are availible for columns
<table>
Following attributes are available for columns
|| <b class='title' valign='top'>name** || <b class='title' valign='top'>args** || <b class='title'>description** ||
|||||| &raquo;&raquo; Basic attributes ||
|| **primary** || bool true || Defines column as a primary key column. ||
|| **autoincrement** || bool true || Defines column as autoincremented column. If the underlying database doesn't support autoincrementation natively its emulated with triggers and sequence tables.
|| **default** || mixed default || 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.
|| **zerofill** || boolean zerofill || Defines column as zerofilled column. Only supported by some drivers.
|| **unsigned** || boolean true || Defines column with integer type as unsigned. Only supported by some drivers.
|| **fixed** || boolean true || Defines string typed column as fixed length.
|| **enum** || array enum || Sets //enum// as an application level enum value list for a column.
|||||| &raquo;&raquo; Basic validators ||
|| **unique** || bool true || Acts as database level unique constraint. Also validates that the specified column is unique.
|| **nospace** || bool true || Nospace validator. This validator validates that specified column doesn't contain any space/newline characters.
|| **notblank** || bool true || Notblank validator. This validator validates that specified column doesn't contain only space/newline characters. Useful in for example comment posting applications where users are not allowed to post empty comments.
|| **notnull** || bool true || Acts as database level notnull constraint as well as notnull validator for the specified column.
|||||| &raquo;&raquo; Advanced validators ||
|| **email** || bool true || Email validator. Validates that specified column is a valid email address.
|| **date** || bool true || Date validator.
|| **range** || array(min, max) || Range validator. Validates that the column is between //min// and //max//.
|| **country** || bool true || Country code validator validates that specified column has a valid country code.
|| **regexp ** || string regexp || Regular expression validator validates that specified column matches //regexp//.
|| **ip** || bool true || Ip validator validates that specified column is a valid internet protocol address.
|| **usstate** || bool true || Usstate validator validates that specified column is a valid usstate.
<tr>
<td>
<b class='title' valign='top'>name</b>
</td>
<td>
<b class='title' valign='top'>args</b>
</td>
<td>
<b class='title'>description</b>
</td>
</tr>
<tr>
<td colspan=3>
<hr>
</td>
</tr>
<tr>
<td colspan=3>
&raquo;&raquo; Basic attributes
<hr class='small'>
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>primary</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Defines column as a primary key column.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>autoincrement</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Defines column as autoincremented column. If the underlying database doesn't support autoincrementation natively its emulated with triggers and sequence tables.
</td>
</tr>
<tr>
<td class='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>
&raquo;&raquo; 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't contain any space/newline characters. <br />
</td>
</tr>
<tr>
<td class='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't contain only space/newline characters. Useful in for example comment posting applications
where users are not allowed to post empty comments.
</td>
</tr>
<tr>
<td class='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>
&raquo;&raquo; 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.
</td>
</tr>
<tr>
<td class='title' valign='top'>
<b>usstate</b>
</td>
<td class='title' valign='top'>
bool true
</td>
<td class='title' valign='top'>
Usstate validator validates that specified column is a valid usstate.
</td>
</tr>
</table>
<code type="php">
......
Following data types are availible in doctrine:
<ul>
<li /><b> string </b>
<li />** string **
<dd /> The same as type 'string' in php
<li /><b> float / double</b>
<dd /> The same as type 'float' in php<br />
<li /><b> integer</b>
<dd /> The same as type 'integer' in php<br />
<li /><b> boolean </b>
<dd /> The same as type 'boolean' in php<br />
<li /><b> array </b>
<ul> The same as type 'array' in php. Automatically serialized when saved into database and unserialized when retrieved from database.</ul>
<li /><b> object </b>
<ul> The same as type 'object' in php. Automatically serialized when saved into database and unserialized when retrieved from database.</ul>
<li /><b> enum </b>
<ul> </ul>
<li /><b> timestamp </b>
<li />** float / double**
<dd /> The same as type 'float' in php
<li />** integer**
<dd /> The same as type 'integer' in php
<li />** boolean **
<dd /> The same as type 'boolean' in php
<li />** array **
The same as type 'array' in php. Automatically serialized when saved into database and unserialized when retrieved from database.
<li />** object **
The same as type 'object' in php. Automatically serialized when saved into database and unserialized when retrieved from database.
<li />** enum **
<li />** timestamp **
<dd /> Database 'timestamp' type
<li /><b> clob</b>
<li />** clob**
<dd /> Database 'clob' type
<li /><b> blob</b>
<li />** blob**
<dd /> Database 'blob' type
<li /><b> date </b>
<li />** date **
<dd /> Database 'date' type
</ul>
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 \>
as well as application level validated length (the length that is validated with Doctrine validators).
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.
Example 2. Column with type 'integer' and length 1 results in 'TINYINT' on many databases.
<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' on many databases.
<br \><br \>
In general Doctrine is smart enough to know which integer/string type to use depending on the specified length.
<br \>
<code type="php">
class Article extends Doctrine_Record {
......
<?php ?>
Doctrine supports default values for all data types. When default value is attached to a record column this means two of things.
First this value is attached to every newly created Record.
<br \><br \>
<?php
renderCode("<?php
<code type="php">
<?php
class User extends Doctrine_record {
public function setTableDefinition() {
......@@ -13,8 +14,9 @@ class User extends Doctrine_record {
\$user = new User();
print \$user->name; // default name
?>");
?>
<br \>
Also when exporting record class to database DEFAULT <i>value</i> is attached to column definition statement. <br \>
?></code>
Also when exporting record class to database DEFAULT //value// is attached to column definition statement.
......@@ -204,11 +204,13 @@ echo $two->getNode()->getNumberChildren() .'</br>';
output_message('path to 1');
$path = $one->getNode()->getPath(' > ');
echo $path .'<br />';
echo $path .'
';
output_message('path to 1 (including 1)');
$path = $one->getNode()->getPath(' > ', true);
echo $path .'<br />';
echo $path .'
';
output_message('1 has parent');
$hasParent = $one->getNode()->hasParent();
......@@ -219,7 +221,8 @@ output_message('parent to 1');
$parent = $one->getNode()->getParent();
if($parent->exists())
{
echo $parent->get('name') .'<br />';
echo $parent->get('name') .'
';
}
output_message('root isRoot?');
......@@ -241,21 +244,24 @@ output_message('root getParent');
$parent = $root->getNode()->getParent();
if($parent->exists())
{
echo $parent->get('name') .'<br />';
echo $parent->get('name') .'
';
}
output_message('get first child of root');
$record = $root->getNode()->getFirstChild();
if($record->exists())
{
echo $record->get('name') .'<br />';
echo $record->get('name') .'
';
}
output_message('get last child of root');
$record = $root->getNode()->getLastChild();
if($record->exists())
{
echo $record->get('name') .'<br />';
echo $record->get('name') .'
';
}
$one_two->refresh();
......@@ -264,14 +270,16 @@ output_message('get prev sibling of 1.2');
$record = $one_two->getNode()->getPrevSibling();
if($record->exists())
{
echo $record->get('name') .'<br />';
echo $record->get('name') .'
';
}
output_message('get next sibling of 1.2');
$record = $one_two->getNode()->getNextSibling();
if($record->exists())
{
echo $record->get('name') .'<br />';
echo $record->get('name') .'
';
}
output_message('siblings of 1.2');
......@@ -279,7 +287,8 @@ $siblings = $one_two->getNode()->getSiblings();
foreach($siblings as $sibling)
{
if($sibling->exists())
echo $sibling->get('name') .'<br />';
echo $sibling->get('name') .'
';
}
output_message('siblings of 1.2 (including 1.2)');
......@@ -287,7 +296,8 @@ $siblings = $one_two->getNode()->getSiblings(true);
foreach($siblings as $sibling)
{
if($sibling->exists())
echo $sibling->get('name') .'<br />';
echo $sibling->get('name') .'
';
}
$new = new Menu();
......@@ -384,6 +394,8 @@ function output_node($record)
function output_message($msg)
{
echo "<br /><strong><em>$msg</em></strong>".'<br />';
echo "
**//$msg//**".'
';
}
</code>
\ No newline at end of file
One problem with database compatibility is that many databases differ in their behaviour of how the result set of a
query is returned. MySql leaves the field names unchanged, which means if you issue a query of the form
"SELECT myField FROM ..." then the result set will contain the field 'myField'.
<br />
<br />
Unfortunately, this is just the way MySql and some other databases do it. Postgres for example returns all field names in lowercase
whilst Oracle returns all field names in uppercase. "So what? In what way does this influence me when using Doctrine?",
you may ask. Fortunately, you don't have to bother about that issue at all. <br />
<br />Doctrine takes care of this problem
you may ask. Fortunately, you don't have to bother about that issue at all.
Doctrine takes care of this problem
transparently. That means if you define a derived Record class and define a field called 'myField' you will always
access it through $record->myField (or $record['myField'], whatever you prefer) no matter whether you're using MySql
or Postgres or Oracle ect.<br />
<br />
or Postgres or Oracle ect.
In short: You can name your fields however you want, using under_scores, camelCase or whatever you prefer.
The date data type may represent dates with year, month and day. DBMS independent representation of dates is accomplished by using text strings formatted according to the IS0-8601 standard.
<br \><br \>
The format defined by the ISO-8601 standard for dates is YYYY-MM-DD where YYYY is the number of the year (Gregorian calendar), MM is the number of the month from 01 to 12 and DD is the number of the day from 01 to 31. Months or days numbered below 10 should be padded on the left with 0.
<br \><br \>
Some DBMS have native support for date formats, but for others the DBMS driver may have to represent them as integers or text values. In any case, it is always possible to make comparisons between date values as well sort query results by fields of this type.
<br \><br \>
<code type="php">
class Test extends Doctrine_Record {
......
All DBMS provide multiple choice of data types for the information that can be stored in their database table fields.
However, the set of data types made available varies from DBMS to DBMS.
<br \> <br \>
To simplify the interface with the DBMS supported by Doctrine, it was defined a base set of data types
that applications may access independently of the underlying DBMS.
<br \><br \>
The Doctrine applications programming interface takes care of mapping data types
when managing database options. It is also able to convert that is sent to and received from the underlying DBMS using the respective driver.
<br \><br \>
The following data type examples should be used with Doctrine's createTable() method.
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' on many databases.
<br \><br \>
In general Doctrine is smart enough to know which integer/string type to use depending on the specified length.
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 <a href="http://en.wikipedia.org/wiki/Tree_data_structure" >http://en.wikipedia.org/wiki/Tree_data_structure</a></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.
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.
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 [http://en.wikipedia.org/wiki/Tree_data_structure" >http://en.wikipedia.org/wiki/Tree_data_structure]
There are three major approaches to managing tree structures in relational databases, these are:
<p>There are three major approaches to managing tree structures in relational databases, these are:</p>
<ul>
<li>the adjacency list model</li>
<li>the nested set model (otherwise known as the modified pre-order tree traversal algorithm)</li>
<li>materialized path model</li>
</ul>
<p>These are explained in more detail in the following chapters, or see<br />
<a href="http://www.dbazine.com/oracle/or-articles/tropashko4" >http://www.dbazine.com/oracle/or-articles/tropashko4</a>, <a href="http://dev.mysql.com/tech-resources/articles/hierarchical-data.html" >http://dev.mysql.com/tech-resources/articles/hierarchical-data.html</a></p>
\ No newline at end of file
These are explained in more detail in the following chapters, or see
[http://www.dbazine.com/oracle/or-articles/tropashko4 http://www.dbazine.com/oracle/or-articles/tropashko4], [http://dev.mysql.com/tech-resources/articles/hierarchical-data.html http://dev.mysql.com/tech-resources/articles/hierarchical-data.html]
\ No newline at end of file
<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>
\ No newline at end of file
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:
\ No newline at end of file
<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>
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>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>
\ No newline at end of file
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.
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.
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)!
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).
\ No newline at end of file
<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>
\ No newline at end of file
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.
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.
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.
\ No newline at end of file
<p>You can traverse a Tree in different ways, please see here for more information <a href="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>
\ No newline at end of file
You can traverse a Tree in different ways, please see here for more information [http://en.wikipedia.org/wiki/Tree_traversal http://en.wikipedia.org/wiki/Tree_traversal].
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)
\ No newline at end of file
<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>
\ No newline at end of file
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:
\ No newline at end of file
<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>For more information, read here:<br />
<a href="http://www.sitepoint.com/article/hierarchical-data-database/2" >http://www.sitepoint.com/article/hierarchical-data-database/2</a>,
<a href="http://dev.mysql.com/tech-resources/articles/hierarchical-data.html" >http://dev.mysql.com/tech-resources/articles/hierarchical-data.html</a>
</p>
\ No newline at end of file
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.
For more information, read here:
[http://www.sitepoint.com/article/hierarchical-data-database/2 http://www.sitepoint.com/article/hierarchical-data-database/2],
[http://dev.mysql.com/tech-resources/articles/hierarchical-data.html http://dev.mysql.com/tech-resources/articles/hierarchical-data.html]
<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>
\ No newline at end of file
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.
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.
\ No newline at end of file
<p>To set up your model as Nested Set, you must add the following code to your model's table definition.</p>
\ No newline at end of file
To set up your model as Nested Set, you must add the following code to your model's table definition.
\ No newline at end of file
<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>
\ No newline at end of file
The nested implementation can be configured to allow your table to have multiple root nodes, and therefore multiple trees within the same table.
The example below shows how to setup and use multiple roots based upon the set up above:
\ No newline at end of file
<?php ?>
You can add indexes by simple calling Doctrine_Record::index('indexName', $definition) where $definition is the
definition array.
<br \><br \>
An example of adding a simple index to field called 'name':
<br \><br \>
<?php
renderCode("<?php
<code type="php">
class IndexTest extends Doctrine_Record
{
public function setTableDefinition()
......@@ -15,13 +18,15 @@ class IndexTest extends Doctrine_Record
\$this->index('myindex', array('fields' => 'name');
}
}
?>");
?>
<br \><br \>
?></code>
An example of adding a multi-column index to field called 'name':
<br \><br \>
<?php
renderCode("<?php
<code type="php">
class MultiColumnIndexTest extends Doctrine_Record
{
public function setTableDefinition()
......@@ -32,13 +37,15 @@ class MultiColumnIndexTest extends Doctrine_Record
\$this->index('myindex', array('fields' => array('name', 'code')));
}
}
?>");
?>
<br \><br \>
?></code>
An example of adding a multiple indexes on same table:
<br \><br \>
<?php
renderCode("<?php
<code type="php">
class MultipleIndexTest extends Doctrine_Record
{
public function setTableDefinition()
......@@ -51,5 +58,4 @@ class MultipleIndexTest extends Doctrine_Record
\$this->index('ageindex', array('fields' => array('age'));
}
}
?>");
?>
?></code>
<?php ?>
Doctrine offers many index options, some of them being db-specific. Here is a full list of availible options:
<div class='sql'>
<pre>
<code>
sorting => string('ASC' / 'DESC')
what kind of sorting does the index use (ascending / descending)
......@@ -16,10 +15,8 @@ type => string('unique', -- supported by most drivers
'fulltext', -- only availible on Mysql driver
'gist', -- only availible on Pgsql driver
'gin') -- only availible on Pgsql driver
</pre>
</div>
<?php
renderCode("<?php
</code>
<code type="php">
class MultipleIndexTest extends Doctrine_Record
{
public function setTableDefinition()
......@@ -38,5 +35,4 @@ class MultipleIndexTest extends Doctrine_Record
));
}
}
?>");
?>
?></code>
Indexes are used to find rows with specific column values quickly.
Without an index, the database must begin with the first row and then read through the entire table to find the relevant rows.
<br \><br \>
The larger the table, the more this consumes time. If the table has an index for the columns in question, the database
can quickly determine the position to seek to in the middle of the data file without having to look at all the data.
If a table has 1,000 rows, this is at least 100 times faster than reading rows one-by-one.
<br \><br \>
Indexes come with a cost as they slow down the inserts and updates. However, in general you
should *<b>always</b>* use indexes for the fields that are used in sql where conditions.
should ***always*** use indexes for the fields that are used in sql where conditions.
<?php ?>
Doctrine supports many special indexes. These include Mysql FULLTEXT and Pgsql GiST indexes.
In the following example we define a Mysql FULLTEXT index for the field 'content'.
<br \><br \>
<?php
renderCode("<?php
class Article
......
Composite primary key can be used efficiently in association tables (tables that connect two components together). It is not recommended
to use composite primary keys in anywhere else as Doctrine does not support mapping relations on multiple columns.
<br \><br \>
Due to this fact your doctrine-based system will scale better if it has autoincremented primary key even for association tables.
<code type="php">
......
<?php ?>
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
<code type="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
piece of code to change the formatting:
<br \><br \>
<?php
renderCode("<?php
<code type="php">
\$manager = Doctrine_Manager::getInstance();
\$manager->setAttribute(Doctrine::ATTR_SEQNAME_FORMAT,
'%s_my_seq');
?>");
?>
<br \><br \>
?></code>
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)
you can use the following code:
<br \><br \>
<?php
renderCode("<?php
<code type="php">
\$manager = Doctrine_Manager::getInstance();
\$manager->setAttribute(Doctrine::ATTR_SEQCOL_NAME,
'my_seq_column');
?>");
?>
<br \><br \>
?></code>
In the following example we do not wish to change global configuration we just want to make the id column to use sequence table called
book_sequence. It can be done as follows: <br \><br \>
<?php
renderCode("<?php
book_sequence. It can be done as follows:
<code type="php">
class Book extends Doctrine_Record {
public function setTableDefinition()
{
......@@ -51,12 +62,14 @@ class Book extends Doctrine_Record {
\$this->hasColumn('name', 'string');
}
}
?>");
?>
<br \><br \>
Here we take the preceding example a little further: we want to have a custom sequence column. Here it goes:<br \><br \>
<?php
renderCode("<?php
?></code>
Here we take the preceding example a little further: we want to have a custom sequence column. Here it goes:
<code type="php">
class Book extends Doctrine_Record {
public function setTableDefinition()
{
......@@ -64,6 +77,5 @@ class Book extends Doctrine_Record {
\$this->hasColumn('name', 'string');
}
}
?>");
?>
?></code>
Doctrine supports aggregates and composites. When binding composites you can use methods Doctrine_Record::ownsOne() and Doctrine_Record::ownsMany(). When binding
aggregates you can use methods Doctrine_Record::hasOne() and Doctrine_Record::hasMany(). Basically using the owns* methods is like adding a database level ON CASCADE DELETE
constraint on related component with an exception that doctrine handles the deletion in application level.
<br \><br \>
In Doctrine if you bind an Email to a User using ownsOne or ownsMany methods, everytime User record calls delete the associated
Email record is also deleted.
<br \><br \>
Then again if you bind an Email to a User using hasOne or hasMany methods, everytime User record calls delete the associated
Email record is NOT deleted.
Binding One-To-One foreign key associations is done with Doctrine_Record::ownsOne() and Doctrine_Record::hasOne() methods.
In the following example user owns one email and has one address. So the relationship between user and email is one-to-one composite.
The relationship between user and address is one-to-one aggregate.
<br \><br \>
The Email component here is mapped to User component's column email_id hence their relation is called LOCALKEY relation.
On the other hand the Address component is mapped to User by it's user_id column hence the relation between User and Address is called
FOREIGNKEY relation.
......
......@@ -2,7 +2,9 @@ When it comes to handling inheritance Doctrine is very smart.
In the following example we have one database table called 'entity'.
Users and groups are both entities and they share the same database table.
The only thing we have to make is 3 records (Entity, Group and User).
<br /><br />
Doctrine is smart enough to know that the inheritance type here is one-table-many-classes.
......
......@@ -2,7 +2,9 @@ In the following example we have three database tables called 'entity', 'user' a
Users and groups are both entities.
The only thing we have to do is write 3 classes (Entity, Group and User) and make iterative
setTableDefinition method calls.
<br /><br />
<code type="php">
class Entity extends Doctrine_Record {
......
If you are coming from relational database background it may be familiar to you
how many-to-many associations are handled: an additional association table is needed.
<br \><br \>
In many-to-many relations the relation between the two components is always an aggregate
relation and the association table is owned by both ends. For example in the case of users and groups
when user is being deleted the groups it belongs to are not being deleted and the associations between this user
and the groups it belongs to are being deleted.
<br \><br \>
Sometimes you may not want that association table rows are being deleted when user / group is being deleted. You can override
this behoviour by setting the relations to association component (in this case Groupuser) explicitly.
<br \><br \>
In the following example we have Groups and Users of which relation is defined as
many-to-many. In this case we also need to define an additional class called Groupuser.
......
Doctrine automatically creates table names from the record class names. For this reason, it is recommended to name your record classes using the following rules:
<ul>
<li>Use CamelCase naming</li>
<li>Underscores are allowed</li>
<li>The first letter must be capitalized</li>
<li>The class name cannot be one of the following (these keywords are reserved in DQL API): <br \>
SELECT, FROM, WHERE, UPDATE, DELETE, JOIN, OUTER, INNER, LEFT, GROUP, ORDER, BY, HAVING,<br \>
FETCH, DISTINCT, OBJECT, NULL, TRUE, FALSE, <br \>
NOT, AND, OR, BETWEEN, LIKE, IN,<br \>
AS, UNKNOWN, EMPTY, MEMBER, OF, IS, ASC, DESC, <br \>
AVG, MAX, MIN, SUM, COUNT,<br \>
MOD, UPPER, LOWER, TRIM, POSITION, <br \>
CHARACTER_LENGTH, CHAR_LENGTH, BIT_LENGTH, CURRENT_TIME, CURRENT_DATE, <br \>
CURRENT_TIMESTAMP, NEW, EXISTS, ALL, ANY, SOME.<br \></li>
</ul>
<li>The class name cannot be one of the following (these keywords are reserved in DQL API):
SELECT, FROM, WHERE, UPDATE, DELETE, JOIN, OUTER, INNER, LEFT, GROUP, ORDER, BY, HAVING,
FETCH, DISTINCT, OBJECT, NULL, TRUE, FALSE,
NOT, AND, OR, BETWEEN, LIKE, IN,
AS, UNKNOWN, EMPTY, MEMBER, OF, IS, ASC, DESC,
AVG, MAX, MIN, SUM, COUNT,
MOD, UPPER, LOWER, TRIM, POSITION,
CHARACTER_LENGTH, CHAR_LENGTH, BIT_LENGTH, CURRENT_TIME, CURRENT_DATE,
CURRENT_TIMESTAMP, NEW, EXISTS, ALL, ANY, SOME.
</li>
Example. My_PerfectClass
<br />
If you need to use a different naming schema, you can override this using the setTableName() method in the setTableDefinition() method.
<?php ?>
Doctrine offers various table options. All table options can be set via Doctrine_Record::option($optionName, $value)
<br \><br \>
For example if you are using Mysql and want to use INNODB tables it can be done as follows:
<br \><br \>
<?php
renderCode("<?php
<code type="php">
class MyInnoDbRecord extends Doctrine_Record
{
public function setTableDefinition()
......@@ -14,12 +17,14 @@ class MyInnoDbRecord extends Doctrine_Record
\$this->option('type', 'INNODB');
}
}
?>");
?> <br \><br \>
?></code>
In the following example we set the collate and character set options:
<br \><br \>
<?php
renderCode("<?php
<code type="php">
class MyCustomOptionRecord extends Doctrine_Record
{
public function setTableDefinition()
......@@ -30,6 +35,5 @@ class MyCustomOptionRecord extends Doctrine_Record
\$this->option('charset', 'utf8');
}
}
?>");
?>
?></code>
In the following example we make a user management system where
<br /><br />
1. Each user and group are entities
<br /><br />
2. User is an entity of type 0
<br /><br />
3. Group is an entity of type 1
<br /><br />
4. Each entity (user/group) has 0-1 email
<br /><br />
5. Each entity has 0-* phonenumbers
<br /><br />
6. If an entity is saved all its emails and phonenumbers are also saved
<br /><br />
7. If an entity is deleted all its emails and phonenumbers are also deleted
<br /><br />
8. When an entity is created and saved a current timestamp will be assigned to 'created' field
<br /><br />
9. When an entity is updated a current timestamp will be assigned to 'updated' field
<br /><br />
10. Entities will always be fetched in batches
<code type="php">
......
GoF [Gang Of Four] design patterns used:
<br \>
<li \><a href="http://www.dofactory.com/Patterns/PatternSingleton.aspx">Singleton</a><br \>
<dd>For forcing only one instance of Doctrine_Manager
<li \><a href="http://www.dofactory.com/Patterns/PatternComposite.aspx">Composite</a><br \>
<dd>For leveled configuration
<li \><a href="http://www.dofactory.com/Patterns/PatternFactory.aspx">Factory</a><br \>
<dd>For connection driver loading and many other things
* : [http://www.dofactory.com/Patterns/PatternSingleton.aspx Singleton] : For forcing only one instance of Doctrine_Manager
<li \><a href="http://www.dofactory.com/Patterns/PatternObserver.aspx">Observer</a><br \>
<dd>For event listening
* : [http://www.dofactory.com/Patterns/PatternComposite.aspx Composite] : For leveled configuration
<li \><a href="http://www.dofactory.com/Patterns/PatternFlyweight.aspx">Flyweight</a><br \>
<dd>For efficient usage of validators
* : [http://www.dofactory.com/Patterns/PatternFactory.aspx Factory] : For connection driver loading and many other things
* : [http://www.dofactory.com/Patterns/PatternObserver.aspx Observer] : For event listening
* : [http://www.dofactory.com/Patterns/PatternFlyweight.aspx Flyweight] : For efficient usage of validators
* : [http://www.dofactory.com/Patterns/PatternFlyweight.aspx Iterator] : For iterating through components [Tables, Connections, Records etc.]
* : [http://www.dofactory.com/Patterns/PatternState.aspx State] : For state-wise connections
* : [http://www.dofactory.com/Patterns/PatternStrategy.aspx Strategy] : For algorithm strategies
<li \><a href="http://www.dofactory.com/Patterns/PatternFlyweight.aspx">Iterator</a><br \>
<dd>For iterating through components [Tables, Connections, Records etc.]
<li \><a href="http://www.dofactory.com/Patterns/PatternState.aspx">State</a><br \>
<dd>For state-wise connections
<li \><a href="http://www.dofactory.com/Patterns/PatternStrategy.aspx">Strategy</a><br \>
<dd>For algorithm strategies
<br \><br \>
Enterprise application design patterns used:
<br \>
<li \><a href="http://www.martinfowler.com/eaaCatalog/activeRecord.html">Active Record</a><br \>
<dd>Doctrine is an implementation of this pattern
<li \><a href="http://www.martinfowler.com/eaaCatalog/unitOfWork.html">UnitOfWork</a><br \>
<dd>For maintaining a list of objects affected in a transaction
<li \><a href="http://www.martinfowler.com/eaaCatalog/identityField.html">Identity Field</a><br \>
<dd>For maintaining the identity between record and database row
<li \><a href="http://www.martinfowler.com/eaaCatalog/metadataMapping.html">Metadata Mapping</a><br \>
<dd>For Doctrine DataDict
<li \><a href="http://www.martinfowler.com/eaaCatalog/dependentMapping.html">Dependent Mapping</a><br \>
<dd>For mapping in general, since all records extend Doctrine_Record which performs all mappings
<li \><a href="http://www.martinfowler.com/eaaCatalog/foreignKeyMapping.html">Foreign Key Mapping</a><br \>
<dd>For one-to-one, one-to-many and many-to-one relationships
<li \><a href="http://www.martinfowler.com/eaaCatalog/associationTableMapping.html">Association Table Mapping</a><br \>
<dd>For association table mapping (most commonly many-to-many relationships)
<li \><a href="http://www.martinfowler.com/eaaCatalog/lazyLoad.html">Lazy Load</a><br \>
<dd>For lazy loading of objects and object properties
<li \><a href="http://www.martinfowler.com/eaaCatalog/queryObject.html">Query Object</a><br \>
<dd>DQL API is actually an extension to the basic idea of Query Object pattern
* : [http://www.martinfowler.com/eaaCatalog/activeRecord.html Active Record] : Doctrine is an implementation of this pattern
* : [http://www.martinfowler.com/eaaCatalog/unitOfWork.html UnitOfWork] : For maintaining a list of objects affected in a transaction
* : [http://www.martinfowler.com/eaaCatalog/identityField.html Identity Field] : For maintaining the identity between record and database row
* : [http://www.martinfowler.com/eaaCatalog/metadataMapping.html Metadata Mapping] : For Doctrine DataDict
* : [http://www.martinfowler.com/eaaCatalog/dependentMapping.html Dependent Mapping] : For mapping in general, since all records extend Doctrine_Record which performs all mappings
* : [http://www.martinfowler.com/eaaCatalog/foreignKeyMapping.html Foreign Key Mapping] : For one-to-one, one-to-many and many-to-one relationships
* : [http://www.martinfowler.com/eaaCatalog/associationTableMapping.html Association Table Mapping] : For association table mapping (most commonly many-to-many relationships)
* : [http://www.martinfowler.com/eaaCatalog/lazyLoad.html Lazy Load] : For lazy loading of objects and object properties
* : [http://www.martinfowler.com/eaaCatalog/queryObject.html Query Object] : DQL API is actually an extension to the basic idea of Query Object pattern
<li \><b class="title">Lazy initialization</b><br \>
* <b class="title">Lazy initialization**
For collection elements
<br \><br \>
<li \><b class="title">Subselect fetching</b><br \>
* <b class="title">Subselect fetching**
Doctrine knows how to fetch collections efficiently using a subselect.
<br \><br \>
<li \><b class="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 \><b class="title">Join fetching</b><br \>
* <b class="title">Join fetching**
Doctrine knows how to fetch complex object graphs using joins and subselects
<br \><br \>
<li \><b class="title">Multiple collection fetching strategies</b><br \>
* <b class="title">Multiple collection fetching strategies**
Doctrine has multiple collection fetching strategies for performance tuning.
<br \><br \>
<li \><b class="title">Dynamic mixing of fetching strategies</b><br \>
* <b class="title">Dynamic mixing of fetching strategies**
Fetching strategies can be mixed and for example users can be fetched in a batch collection while
users' phonenumbers are loaded in offset collection using only one query.
<br \><br \>
<li \><b class="title">Driver specific optimizations</b><br \>
* <b class="title">Driver specific optimizations**
Doctrine knows things like bulk-insert on mysql
<br \><br \>
<li \><b class="title">Transactional single-shot delete</b><br \>
* <b class="title">Transactional single-shot delete**
Doctrine knows how to gather all the primary keys of the pending objects in delete list and performs only one sql delete statement per table.
<br \><br \>
<li \><b class="title">Updating only the modified columns.</b><br \>
* <b class="title">Updating only the modified columns.**
Doctrine always knows which columns have been changed.
<br \><br \>
<li \><b class="title">Never inserting/updating unmodified objects.</b><br \>
* <b class="title">Never inserting/updating unmodified objects.**
Doctrine knows if the the state of the record has changed.
<br \><br \>
<li \><b class="title">PDO for database abstraction</b><br \>
* <b class="title">PDO for database abstraction**
PDO is by far the fastest availible database abstraction layer for php.
<br \><br \>
A database transaction is a unit of interaction with a database management system or similar system that is treated in a coherent
and reliable way independent of other transactions that must be either entirely completed or aborted.
Ideally, a database system will guarantee all of the ACID(Atomicity, Consistency, Isolation, and Durability) properties for each transaction.
<ul>
<li><a href="http://en.wikipedia.org/wiki/Atomicity">Atomicity</a> refers to the ability of the DBMS to guarantee that either all of the tasks of a transaction are performed or none of them are. The transfer of funds can be completed or it can fail for a multitude of reasons, but atomicity guarantees that one account won'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't break the rules, or <i>integrity constraints</i>, 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>
</ul>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Isolation_%28computer_science%29" title="Isolation (computer science)">Isolation</a> refers to the ability of the application to make operations in a transaction appear isolated from all other operations. This means that no operation outside the transaction can ever see the data in an intermediate state; a bank manager can see the transferred funds on one account or the other, but never on botheven if she ran her query while the transfer was still being processed. More formally, isolation means the transaction history (or <a href="http://en.wikipedia.org/wiki/Schedule_%28computer_science%29" title="Schedule (computer science)">schedule</a>) is <a href="http://en.wikipedia.org/wiki/Serializability" title="Serializability">serializable</a>. For performance reasons, this ability is the most often relaxed constraint. See the <a href="/wiki/Isolation_%28computer_science%29" title="Isolation (computer science)">isolation</a> article for more details.</li>
</ul>
<ul>
<li><a href="http://en.wikipedia.org/wiki/Durability_%28computer_science%29" title="Durability (computer science)">Durability</a> refers to the guarantee that once the user has been notified of success, the transaction will persist, and not be undone. This means it will survive system failure, and that the <a href="http://en.wikipedia.org/wiki/Database_system" title="Database system">database system</a> has checked the integrity constraints and won'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>
</ul>
- <i>from <a href="http://www.wikipedia.org">wikipedia</a></i>
<br \><br \>
<li>[http://en.wikipedia.org/wiki/Atomicity Atomicity] refers to the ability of the DBMS to guarantee that either all of the tasks of a transaction are performed or none of them are. The transfer of funds can be completed or it can fail for a multitude of reasons, but atomicity guarantees that one account won'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't break the rules, 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/Isolation_%28computer_science%29 Isolation] refers to the ability of the application to make operations in a transaction appear isolated from all other operations. This means that no operation outside the transaction can ever see the data in an intermediate state; a bank manager can see the transferred funds on one account or the other, but never on botheven if she ran her query while the transfer was still being processed. More formally, isolation means the transaction history (or [http://en.wikipedia.org/wiki/Schedule_%28computer_science%29 schedule]) is [http://en.wikipedia.org/wiki/Serializability serializable]. For performance reasons, this ability is the most often relaxed constraint. See the [/wiki/Isolation_%28computer_science%29 isolation] article for more details.</li>
<li>[http://en.wikipedia.org/wiki/Durability_%28computer_science%29 Durability] refers to the guarantee that once the user has been notified of success, the transaction will persist, and not be undone. This means it will survive system failure, and that the [http://en.wikipedia.org/wiki/Database_system database system] has checked the integrity constraints and won'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:
<ul>
<li> Doctrine uses application level transaction nesting.
</ul>
<ul>
<li> Doctrine always executes INSERT / UPDATE / DELETE queries at the end of transaction (when the outermost commit is called). The operations
are performed in the following order: all inserts, all updates and last all deletes. Doctrine knows how to optimize the deletes so that
delete operations of the same component are gathered in one query.
</ul>
<code type="php">
......
......@@ -3,19 +3,28 @@ A transaction isolation level sets the default transactional behaviour.
As the name 'isolation level' suggests, the setting determines how isolated each transation is,
or what kind of locks are associated with queries inside a transaction.
The four availible levels are (in ascending order of strictness):
<br \><br \>
<i>READ UNCOMMITTED</i>: Barely transactional, this setting allows for so-called 'dirty reads',
//READ UNCOMMITTED//: Barely transactional, this setting allows for so-called 'dirty reads',
where queries inside one transaction are affected by uncommitted changes in another transaction.
<br \><br \>
<i>READ COMMITTED</i>: Committed updates are visible within another transaction.
//READ COMMITTED//: Committed updates are visible within another transaction.
This means identical queries within a transaction can return differing results. This is the default in some DBMS's.
<br \> <br \>
<i>REPEATABLE READ</i>: Within a transaction, all reads are consistent. This is the default of Mysql INNODB engine.
<br \><br \>
<i>SERIALIZABLE</i>: Updates are not permitted in other transactions if a transaction has run an ordinary SELECT query.
<br \><br \>
<?php
renderCode("<?php
//REPEATABLE READ//: Within a transaction, all reads are consistent. This is the default of Mysql INNODB engine.
//SERIALIZABLE//: Updates are not permitted in other transactions if a transaction has run an ordinary SELECT query.
<code type="php">
\$tx = \$conn->transaction; // get the transaction module
// sets the isolation level to READ COMMITTED
......@@ -27,6 +36,5 @@ renderCode("<?php
// Some drivers (like Mysql) support the fetching of current transaction
// isolation level. It can be done as follows:
\$level = \$tx->getIsolation();
?>");
?>
?></code>
<?php ?>
Doctrine supports transaction savepoints. This means you can set named transactions and have them nested.
<br \><br \>
The Doctrine_Transaction::beginTransaction(<i>$savepoint</i>) sets a named transaction savepoint with a name of <i>$savepoint</i>.
The Doctrine_Transaction::beginTransaction(//$savepoint//) sets a named transaction savepoint with a name of //$savepoint//.
If the current transaction has a savepoint with the same name, the old savepoint is deleted and a new one is set.
<br \><br \>
<?php
renderCode("<?php
<code type="php">
try {
\$conn->beginTransaction();
// do some operations here
......@@ -23,22 +26,28 @@ try {
} catch(Exception \$e) {
\$conn->rollback();
}
?>");
?>
?></code>
<br \><br \>
The Doctrine_Transaction::rollback(<i>$savepoint</i>) rolls back a transaction to the named savepoint.
The Doctrine_Transaction::rollback(//$savepoint//) rolls back a transaction to the named savepoint.
Modifications that the current transaction made to rows after the savepoint was set are undone in the rollback.
NOTE: Mysql, for example, does not release the row locks that were stored in memory after the savepoint.
<br \><br \>
Savepoints that were set at a later time than the named savepoint are deleted.
<br \><br \>
The Doctrine_Transaction::commit(<i>$savepoint</i>) removes the named savepoint from the set of savepoints of the current transaction.
<br \><br \>
The Doctrine_Transaction::commit(//$savepoint//) removes the named savepoint from the set of savepoints of the current transaction.
All savepoints of the current transaction are deleted if you execute a commit or rollback is being called without savepoint name parameter.
<?php
renderCode("<?php
<code type="php">
try {
\$conn->beginTransaction();
// do some operations here
......@@ -52,6 +61,5 @@ try {
} catch(Exception \$e) {
\$conn->rollback(); // deletes all savepoints
}
?>");
?>
?></code>
When accessing single elements of the collection and those elements (records) don't exist Doctrine auto-adds them.
<br \><br \>
In the following example
we fetch all users from database (there are 5) and then add couple of users in the collection.
<br \><br \>
As with PHP arrays the indexes start from zero.
<code type="php">
......
Doctrine Collections can be deleted in very same way is Doctrine Records you just call delete() method.
As for all collections Doctrine knows how to perform single-shot-delete meaning it only performs one
database query for the each collection.
<br \> <br \>
For example if we have collection of users which own [0-*] phonenumbers. When deleting the collection
of users doctrine only performs two queries for this whole transaction. The queries would look something like:
<br \><br \>
DELETE FROM user WHERE id IN (1,2,3, ... ,N)<br \>
DELETE FROM phonenumber WHERE id IN (1,2,3, ... ,M)<br \>
<br \><br \>
DELETE FROM user WHERE id IN (1,2,3, ... ,N)
DELETE FROM phonenumber WHERE id IN (1,2,3, ... ,M)
It should also be noted that Doctrine is smart enough to perform single-shot-delete per table when transactions are used.
So if you are deleting a lot of records and want to optimize the operation just wrap the delete calls in Doctrine_Connection transaction.
......
......@@ -2,43 +2,72 @@ Whenever you fetch records with eg. Doctrine_Table::findAll or Doctrine_Connecti
Doctrine_Collection is returned. There are many types of collections in Doctrine and it is crucial to understand
the differences of these collections. Remember choosing the right fetching strategy (collection type) is one of the most
influental things when it comes to boosting application performance.
<br \><br \>
<li>Immediate Collection<ul>
<li>Immediate Collection
Fetches all records and all record data immediately into collection memory. Use this collection only if you really need to show all that data
in web page.
<br \><br \>
Example query:<br \>
Example query:
SELECT id, name, type, created FROM user
<br \><br \></ul>
<li>Batch Collection<ul>
<li>Batch Collection
Fetches all record primary keys into colletion memory. When individual collection elements are accessed this collection initializes proxy objects.
When the non-primary-key-property of a proxy object is accessed that object sends request to Batch collection which loads the data
for that specific proxy object as well as other objects close to that proxy object.
<br \><br \>
Example queries:<br \>
SELECT id FROM user<br \>
SELECT id, name, type, created FROM user WHERE id IN (1,2,3,4,5)<br \>
SELECT id, name, type, created FROM user WHERE id IN (6,7,8,9,10)<br \>
[ ... ]<br \>
</ul>
<li>Lazy Collection<ul>
Example queries:
SELECT id FROM user
SELECT id, name, type, created FROM user WHERE id IN (1,2,3,4,5)
SELECT id, name, type, created FROM user WHERE id IN (6,7,8,9,10)
[ ... ]
<li>Lazy Collection
Lazy collection is exactly same as Batch collection with batch size preset to one.
<br \><br \>
Example queries:<br \>
SELECT id FROM user<br \>
SELECT id, name, type, created FROM user WHERE id = 1<br \>
SELECT id, name, type, created FROM user WHERE id = 2<br \>
SELECT id, name, type, created FROM user WHERE id = 3<br \>
[ ... ]<br \>
</ul>
<li>Offset Collection<ul>
Example queries:
SELECT id FROM user
SELECT id, name, type, created FROM user WHERE id = 1
SELECT id, name, type, created FROM user WHERE id = 2
SELECT id, name, type, created FROM user WHERE id = 3
[ ... ]
<li>Offset Collection
Offset collection is the same as immediate collection with the difference that it uses database provided limiting of queries.
<br \><br \>
Example queries:<br \>
SELECT id, name, type, created FROM user LIMIT 5<br \>
SELECT id, name, type, created FROM user LIMIT 5 OFFSET 5<br \>
SELECT id, name, type, created FROM user LIMIT 5 OFFSET 10<br \>
[ ... ]<br \></ul>
Example queries:
SELECT id, name, type, created FROM user LIMIT 5
SELECT id, name, type, created FROM user LIMIT 5 OFFSET 5
SELECT id, name, type, created FROM user LIMIT 5 OFFSET 10
[ ... ]
<code type="php">
......
Doctrine_Collection is a collection of records (see Doctrine_Record). As with records the collections can be deleted and saved using
Doctrine_Collection::delete() and Doctrine_Collection::save() accordingly.
<br \><br \>
When fetching data from database with either DQL API (see Doctrine_Query) or rawSql API (see Doctrine_RawSql) the methods return an instance of
Doctrine_Collection by default.
<br \><br \>
The following example shows how to initialize a new collection:
<code type="php">
......
Doctrine has drivers for every PDO-supported database. The supported databases are:
<ul>
<li \>FreeTDS / Microsoft SQL Server / Sybase
<li \>Firebird/Interbase 6
<li \>Informix
<li \>Mysql
<li \>Oracle
<li \>Odbc
<li \>PostgreSQL
<li \>Sqlite
</ul>
* FreeTDS / Microsoft SQL Server / Sybase
* Firebird/Interbase 6
* Informix
* Mysql
* Oracle
* Odbc
* PostgreSQL
* Sqlite
Doctrine_Connection is a wrapper for database connection. It handles several things:
<ul>
<li \> Handles database portability things missing from PDO (eg. LIMIT / OFFSET emulation)
<li \> Keeps track of Doctrine_Table objects
* Handles database portability things missing from PDO (eg. LIMIT / OFFSET emulation)
<li \> Keeps track of records
* Keeps track of Doctrine_Table objects
<li \> Keeps track of records that need to be updated / inserted / deleted
* Keeps track of records
<li \> Handles transactions and transaction nesting
* Keeps track of records that need to be updated / inserted / deleted
<li \> Handles the actual querying of the database in the case of INSERT / UPDATE / DELETE operations
* Handles transactions and transaction nesting
<li \> Can query the database using the DQL API (see Doctrine_Query)
* Handles the actual querying of the database in the case of INSERT / UPDATE / DELETE operations
<li \> Optionally validates transactions using Doctrine_Validator and gives
* Can query the database using the DQL API (see Doctrine_Query)
* Optionally validates transactions using Doctrine_Validator and gives
full information of possible errors.
</ul>
Doctrine_Db supports event listener chaining. It means multiple listeners can be attached for
listening the events of a single instance of Doctrine_Db.
<br \><br \>
For example you might want to add different aspects to your Doctrine_Db instance on-demand. These aspects may include
caching, query profiling etc.
<br \><br \>
<code type="php">
......
<?php ?>
Doctrine_Db allows both PEAR-like DSN (data source name) as well as PDO like DSN as constructor parameters.
<br \><br \>
Getting an instance of Doctrine_Db using PEAR-like DSN:
<br \><br \>
<?php
$str = "<?php
// using PEAR like dsn for connecting pgsql database
......@@ -15,9 +19,13 @@ $str = "<?php
?>";
renderCode($str);
?>
<br \><br \>
Getting an instance of Doctrine_Db using PDO-like DSN (PDO mysql driver):
<br \><br \>
<?php
$str = "<?php
\$dbh = new Doctrine_Db('mysql:host=localhost;dbname=test',
......@@ -25,16 +33,22 @@ $str = "<?php
?>";
renderCode($str);
?>
<br \><br \>
Getting an instance of Doctrine_Db using PDO-like DSN (PDO sqlite with memory tables):
<br \> <br \>
<?php
$str = "<?php
\$dbh = new Doctrine_Db('sqlite::memory:');
?>";
renderCode($str);
?>
<br \><br \>
Handling connection errors:
......@@ -48,7 +62,8 @@ try {
}
\$dbh = null;
} catch (PDOException \$e) {
print 'Error!: ' . \$e->getMessage() . '<br />';
print 'Error!: ' . \$e->getMessage() . '
';
die();
}
?>";
......
Doctrine_Db is a wrapper for PDO database object. Why should you consider using Doctrine_Db instead of PDO?
<br \><br \>
1. It provides efficient eventlistener architecture, hence its easy to add new aspects to existing methods like on-demand-caching
<br \><br \>
2. Doctrine_Db lazy-connects database. Creating an instance of Doctrine_Db doesn't directly connect database, hence
Doctrine_Db fits perfectly for application using for example page caching.
<br \><br \>
3. It has many short cuts for commonly used fetching methods like Doctrine_Db::fetchOne().
<br \><br \>
4. Supports PEAR-like data source names as well as PDO data source names.
......@@ -2,10 +2,14 @@
Doctrine_Db has a pluggable event listener architecture. It provides before and after
listeners for all relevant methods. Every listener method takes one parameter: a Doctrine_Db_Event object, which
holds info about the occurred event.
<br \><br \>
Every listener object must either implement the Doctrine_Db_EventListener_Interface or Doctrine_Overloadable interface.
Using Doctrine_Overloadable interface
only requires you to implement __call() which is then used for listening all the events. <br \><br \>
only requires you to implement __call() which is then used for listening all the events.
<?php
$str = "<?php
......@@ -17,10 +21,14 @@ class OutputLogger extends Doctrine_Overloadable {
?>";
renderCode($str);
?>
<br \><br \>
For convience
you may want to make your listener class extend Doctrine_Db_EventListener which has empty listener methods, hence allowing you not to define
all the listener methods by hand. The following listener, 'MyLogger', is used for listening only onPreQuery and onQuery methods.<br \><br \>
all the listener methods by hand. The following listener, 'MyLogger', is used for listening only onPreQuery and onQuery methods.
<?php
$str = "<?php
class MyLogger extends Doctrine_Db_EventListener {
......@@ -34,9 +42,13 @@ class MyLogger extends Doctrine_Db_EventListener {
?>";
renderCode($str);
?>
<br \><br \>
Now the next thing we need to do is bind the eventlistener objects to our database handler.
<br \><br \>
<code type="php">
......
......@@ -35,17 +35,22 @@ foreach($e as $line) {
elseif(strpos($line, "DQL") !== false)
$color = "367FAC";
$l = str_replace("SELECT","<br \><font color='$color'><b>SELECT</b></font>",$line);
$l = str_replace("FROM","<br \><font color='$color'><b>FROM</b></font>",$l);
$l = str_replace("LEFT JOIN","<br \><font color='$color'><b>LEFT JOIN</b></font>",$l);
$l = str_replace("INNER JOIN","<br \><font color='$color'><b>INNER JOIN</b></font>",$l);
$l = str_replace("WHERE","<br \><font color='$color'><b>WHERE</b></font>",$l);
$l = str_replace("AS","<font color='$color'><b>AS</b></font>",$l);
$l = str_replace("ON","<font color='$color'><b>ON</b></font>",$l);
$l = str_replace("ORDER BY","<font color='$color'><b>ORDER BY</b></font>",$l);
$l = str_replace("LIMIT","<font color='$color'><b>LIMIT</b></font>",$l);
$l = str_replace("OFFSET","<font color='$color'><b>OFFSET</b></font>",$l);
$l = str_replace("DISTINCT","<font color='$color'><b>DISTINCT</b></font>",$l);
$l = str_replace("SELECT","
<font color='$color'>**SELECT**</font>",$line);
$l = str_replace("FROM","
<font color='$color'>**FROM**</font>",$l);
$l = str_replace("LEFT JOIN","
<font color='$color'>**LEFT JOIN**</font>",$l);
$l = str_replace("INNER JOIN","
<font color='$color'>**INNER JOIN**</font>",$l);
$l = str_replace("WHERE","
<font color='$color'>**WHERE**</font>",$l);
$l = str_replace("AS","<font color='$color'>**AS**</font>",$l);
$l = str_replace("ON","<font color='$color'>**ON**</font>",$l);
$l = str_replace("ORDER BY","<font color='$color'>**ORDER BY**</font>",$l);
$l = str_replace("LIMIT","<font color='$color'>**LIMIT**</font>",$l);
$l = str_replace("OFFSET","<font color='$color'>**OFFSET**</font>",$l);
$l = str_replace("DISTINCT","<font color='$color'>**DISTINCT**</font>",$l);
$l = str_replace(" ","<dd>",$l);
print $l."<br>";
......
......@@ -3,9 +3,10 @@ The FROM clause indicates the component or components from which to retrieve rec
If you name more than one component, you are performing a join.
For each table specified, you can optionally specify an alias. Doctrine_Query offers easy to use
methods such as from(), addFrom(), leftJoin() and innerJoin() for managing the FROM part of your DQL query.
<br \><br \>
<?php
renderCode("<?php
<code type="php">
// find all users
\$q = new Doctrine_Query();
......@@ -14,9 +15,12 @@ renderCode("<?php
// find all users with only their names (and primary keys) fetched
\$coll = \$q->select('u.name')->('User u');
?>");
?> <br \><br \>
The following example shows how to use leftJoin and innerJoin methods: <br \><br \>
?></code>
The following example shows how to use leftJoin and innerJoin methods:
<code type="php">
// find all groups
......
<?php ?>
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().
<code type="php">
\$q = new Doctrine_Query();
\$users = \$q->select('u.name')
->from('User u')
->leftJoin('u.Phonenumber p');
->having('COUNT(p.id) > 3');
?>");
?>
?></code>
......@@ -3,7 +3,9 @@ DQL (Doctrine Query Language) is a object query language which allows
you to find objects. DQL understands things like object relationships, polymorphism and
inheritance (including column aggregation inheritance).
For more info about DQL see the actual DQL chapter.
<br \><br \>
Doctrine_Query along with Doctrine_Expression provide an easy-to-use wrapper for writing DQL queries. Creating a new
query object can be done by either using the new operator or by calling create method. The create method exists for allowing easy
method call chaining.
......
Doctrine provides two relation operators: '.' aka dot and ':' aka colon.
<br \><br \>
The dot-operator is used for SQL LEFT JOINs and the colon-operator is used
for SQL INNER JOINs. Basically you should use dot operator if you want for example
to select all users and their phonenumbers AND it doesn't matter if the users actually have any phonenumbers.
<br \><br \>
On the other hand if you want to select only the users which actually have phonenumbers you should use the colon-operator.
<code type="php">
......
......@@ -3,9 +3,9 @@ The WHERE clause, if given, indicates the condition or conditions that the recor
Doctrine_Query provides easy to use WHERE -part management methods where and addWhere. The where methods always overrides
the query WHERE -part whereas addWhere adds new condition to the WHERE -part stack.
<br \>
<?php
renderCode("<?php
<code type="php">
// find all groups where the group primary key is bigger than 10
\$coll = \$q->from('Group')->where('Group.id > 10');
......@@ -13,23 +13,26 @@ renderCode("<?php
// the same query using Doctrine_Expression component
\$e = \$q->expr;
\$coll = \$q->from('Group')->where(\$e->gt('Group.id', 10));
?>");
?>
<br \><br \>
Using regular expression operator: <br \><br \>
<?php
renderCode("<?php
?></code>
Using regular expression operator:
<code type="php">
// find all users where users where user name matches
// a regular expression, regular expressions must be
// supported by the underlying database
\$coll = \$conn->query(\"FROM User WHERE User.name REGEXP '[ad]'\");
?>");
?>
<br \><br \>
DQL has support for portable LIKE operator: <br \><br \>
<?php
renderCode("<?php
\$coll = \$conn->query(\"FROM User WHERE User.name REGEXP '[ad]'\</code></code>
DQL has support for portable LIKE operator:
<code type="php">
// find all users and their associated emails
// where SOME of the users phonenumbers
// (the association between user and phonenumber
......@@ -37,13 +40,14 @@ renderCode("<?php
\$coll = \$q->select('u.*, e.*')
->from('User u LEFT JOIN u.Email e LEFT JOIN u.Phonenumber p')
->where(\"p.phonenumber LIKE '123%'\");
?>");
?>
<br \><br \>
Using multiple conditions and condition nesting are also possible: <br \><br \>
<?php
renderCode("<?php
->where(\"p.phonenumber LIKE '123%'\</code></code>
Using multiple conditions and condition nesting are also possible:
<code type="php">
// multiple conditions
\$coll = \$q->select('u.*')
......@@ -54,8 +58,6 @@ renderCode("<?php
\$coll = \$q->select('u.*')
->from('User u LEFT JOIN u.Email e')
->where(\"u.name LIKE '%Jack%' OR u.name LIKE '%John%') AND e.address LIKE '%@drinkmore.info'\");
?>");
?>
->where(\"u.name LIKE '%Jack%' OR u.name LIKE '%John%') AND e.address LIKE '%@drinkmore.info'\</code></code>
The following example represents a bit harder case where we select all entities and their associated phonenumbers using a left join. Again we
wrap all the columns in curly brackets but we also specify what tables associate to which components.
<br \> <br \>
First we specify that table entity maps to record class 'Entity'
<br \><br \>
Then we specify that table phonenumber maps to Entity.Phonenumber (meaning phonenumber associated with an entity)
<code type="php">
......
In Doctrine you may express your queries in the native SQL dialect of your database.
This is useful if you want to use the full power of your database vendor's features (like query hints or the CONNECT keyword in Oracle).
<br \><br \>
It should be noted that not all the sql is portable. So when you make database portable applications you might want to use the DQL API instead.
The rawSql component works in much same way as Zend_Db_Select. You may use method overloading like $q->from()->where() or just use
$q->parseQuery(). There are some differences though:
<br \><br \>
1. In Doctrine_RawSql component you need to specify all the mapped table columns in curly brackets {} this is used for smart column aliasing.
<br \><br \>
2. When joining multiple tables you need to specify the component paths with addComponent() method
<br \><br \>
The following example represents a very simple case where no addComponent() calls are needed.
Here we select all entities from table entity with all the columns loaded in the records.
......
There are three possible ways to access the properties of a record (fields of database row).
You can use overloading, ArrayAccess interface or simply Doctrine_Record::get() method.
<b>Doctrine_Record objects have always all properties in lowercase</b>.
**Doctrine_Record objects have always all properties in lowercase**.
......@@ -2,7 +2,9 @@ Every Doctrine_Record has a state. First of all record can be transient or persi
Every record that is retrieved from database is persistent and every newly created record is transient.
If a Doctrine_Record is retrieved from database but the only loaded property is its primary key, then this record
has a state called proxy.
<br /><br />
Every transient and persistent Doctrine_Record is either clean or dirty. Doctrine_Record is clean when none of its properties are changed and
dirty when atleast one of its properties has changed.
......
Doctrine_Record is a wrapper for database row but along with that it speficies what relations it has
on other components and what columns it has. It may access the related components, hence its refered as an ActiveRecord.
<br \><br \>
The classes that inherit Doctrine_Record are called components. There should be atleast one component for each database table.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment