Commit 876973a8 authored by hansbrix's avatar hansbrix

html->wiki conversion script

parent c4ca5964
Caching is one of the most influental things when it comes to performance tuning. Doctrine_Cache provides means for Caching is one of the most influental things when it comes to performance tuning. Doctrine_Cache provides means for
caching queries and for managing the cached queries. caching queries and for managing the cached queries.
<?php ?> <?php ?>
Doctrine_Db_Profiler is an eventlistener for Doctrine_Db. It provides flexible query profiling. Besides the sql strings 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 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. 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 Doctrine_Db_Profiler can be enabled by adding it as an eventlistener for Doctrine_Db.
renderCode("<?php
?>");
?> <code type="php">
?></code>
<code type="php"> <code type="php">
class User { class User {
public function setTableDefinition() { public function setTableDefinition() {
$this->hasColumn("name", "string", 200); $this->hasColumn("name", "string", 200);
$this->hasColumn("password", "string", 32); $this->hasColumn("password", "string", 32);
} }
public function setPassword($password) { public function setPassword($password) {
return md5($password); return md5($password);
} }
public function getName($name) { public function getName($name) {
return strtoupper($name); return strtoupper($name);
} }
} }
$user = new User(); $user = new User();
$user->name = 'someone'; $user->name = 'someone';
print $user->name; // someone print $user->name; // someone
$user->password = '123'; $user->password = '123';
print $user->password; // 123 print $user->password; // 123
$user->setAttribute(Doctrine::ATTR_LISTENER, new Doctrine_EventListener_AccessorInvoker()); $user->setAttribute(Doctrine::ATTR_LISTENER, new Doctrine_EventListener_AccessorInvoker());
print $user->name; // SOMEONE print $user->name; // SOMEONE
$user->password = '123'; $user->password = '123';
print $user->password; // 202cb962ac59075b964b07152d234b70 print $user->password; // 202cb962ac59075b964b07152d234b70
</code> </code>
Creating a new listener is very easy. You can set the listener in global, connection or factory level. Creating a new listener is very easy. You can set the listener in global, connection or factory level.
<code type="php"> <code type="php">
class MyListener extends Doctrine_EventListener { class MyListener extends Doctrine_EventListener {
public function onLoad(Doctrine_Record $record) { public function onLoad(Doctrine_Record $record) {
print $record->getTable()->getComponentName()." just got loaded!"; print $record->getTable()->getComponentName()." just got loaded!";
} }
public function onSave(Doctrine_Record $record) { public function onSave(Doctrine_Record $record) {
print "saved data access object!"; print "saved data access object!";
} }
} }
class MyListener2 extends Doctrine_EventListener { class MyListener2 extends Doctrine_EventListener {
public function onPreUpdate() { public function onPreUpdate() {
try { try {
$record->set("updated",time()); $record->set("updated",time());
} catch(InvalidKeyException $e) { } catch(InvalidKeyException $e) {
} }
} }
} }
// setting global listener // setting global listener
$manager = Doctrine_Manager::getInstance(); $manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_LISTENER,new MyListener()); $manager->setAttribute(Doctrine::ATTR_LISTENER,new MyListener());
// setting connection level listener // setting connection level listener
$conn = $manager->openConnection($dbh); $conn = $manager->openConnection($dbh);
$conn->setAttribute(Doctrine::ATTR_LISTENER,new MyListener2()); $conn->setAttribute(Doctrine::ATTR_LISTENER,new MyListener2());
// setting factory level listener // setting factory level listener
$table = $conn->getTable("User"); $table = $conn->getTable("User");
$table->setAttribute(Doctrine::ATTR_LISTENER,new MyListener()); $table->setAttribute(Doctrine::ATTR_LISTENER,new MyListener());
</code> </code>
Here is a list of availible events and their parameters: Here is a list of availible events and their parameters:
<code type="php"> <code type="php">
interface Doctrine_EventListener_Interface { interface Doctrine_EventListener_Interface {
public function onLoad(Doctrine_Record $record); public function onLoad(Doctrine_Record $record);
public function onPreLoad(Doctrine_Record $record); public function onPreLoad(Doctrine_Record $record);
public function onUpdate(Doctrine_Record $record); public function onUpdate(Doctrine_Record $record);
public function onPreUpdate(Doctrine_Record $record); public function onPreUpdate(Doctrine_Record $record);
public function onCreate(Doctrine_Record $record); public function onCreate(Doctrine_Record $record);
public function onPreCreate(Doctrine_Record $record); public function onPreCreate(Doctrine_Record $record);
public function onSave(Doctrine_Record $record); public function onSave(Doctrine_Record $record);
public function onPreSave(Doctrine_Record $record); public function onPreSave(Doctrine_Record $record);
public function onInsert(Doctrine_Record $record); public function onInsert(Doctrine_Record $record);
public function onPreInsert(Doctrine_Record $record); public function onPreInsert(Doctrine_Record $record);
public function onDelete(Doctrine_Record $record); public function onDelete(Doctrine_Record $record);
public function onPreDelete(Doctrine_Record $record); public function onPreDelete(Doctrine_Record $record);
public function onEvict(Doctrine_Record $record); public function onEvict(Doctrine_Record $record);
public function onPreEvict(Doctrine_Record $record); public function onPreEvict(Doctrine_Record $record);
public function onSleep(Doctrine_Record $record); public function onSleep(Doctrine_Record $record);
public function onWakeUp(Doctrine_Record $record); public function onWakeUp(Doctrine_Record $record);
public function onClose(Doctrine_Connection $connection); public function onClose(Doctrine_Connection $connection);
public function onPreClose(Doctrine_Connection $connection); public function onPreClose(Doctrine_Connection $connection);
public function onOpen(Doctrine_Connection $connection); public function onOpen(Doctrine_Connection $connection);
public function onTransactionCommit(Doctrine_Connection $connection); public function onTransactionCommit(Doctrine_Connection $connection);
public function onPreTransactionCommit(Doctrine_Connection $connection); public function onPreTransactionCommit(Doctrine_Connection $connection);
public function onTransactionRollback(Doctrine_Connection $connection); public function onTransactionRollback(Doctrine_Connection $connection);
public function onPreTransactionRollback(Doctrine_Connection $connection); public function onPreTransactionRollback(Doctrine_Connection $connection);
public function onTransactionBegin(Doctrine_Connection $connection); public function onTransactionBegin(Doctrine_Connection $connection);
public function onPreTransactionBegin(Doctrine_Connection $connection); public function onPreTransactionBegin(Doctrine_Connection $connection);
public function onCollectionDelete(Doctrine_Collection $collection); public function onCollectionDelete(Doctrine_Collection $collection);
public function onPreCollectionDelete(Doctrine_Collection $collection); public function onPreCollectionDelete(Doctrine_Collection $collection);
} }
<code type="php"> <code type="php">
$table = $conn->getTable("User"); $table = $conn->getTable("User");
$table->setEventListener(new MyListener2()); $table->setEventListener(new MyListener2());
// retrieve user whose primary key is 2 // retrieve user whose primary key is 2
$user = $table->find(2); $user = $table->find(2);
$user->name = "John Locke"; $user->name = "John Locke";
// update event will be listened and current time will be assigned to the field 'updated' // update event will be listened and current time will be assigned to the field 'updated'
$user->save(); $user->save();
</code> </code>
Many web applications have different kinds of lists. The lists may contain data from multiple components (= database tables) and Many web applications have different kinds of lists. The lists may contain data from multiple components (= database tables) and
they may have actions such as paging, sorting and setting conditions. Doctrine_Hook helps building these lists. It has a simple API for they may have actions such as paging, sorting and setting conditions. Doctrine_Hook helps building these lists. It has a simple API for
building search criteria forms as well as building a DQL query from the 'hooked' parameters. building search criteria forms as well as building a DQL query from the 'hooked' parameters.
<code type="php"> <code type="php">
$hook = new Doctrine_Hook($table, $fields); $hook = new Doctrine_Hook($table, $fields);
</code> </code>
The following code snippet demonstrates the use of Doctrine's pessimistic offline locking capabilities. The following code snippet demonstrates the use of Doctrine's pessimistic offline locking capabilities.
At the page where the lock is requested... At the page where the lock is requested...
<code type="php"> <code type="php">
// Get a locking manager instance // Get a locking manager instance
$lockingMngr = new Doctrine_Locking_Manager_Pessimistic(); $lockingMngr = new Doctrine_Locking_Manager_Pessimistic();
try try
{ {
// Ensure that old locks which timed out are released // Ensure that old locks which timed out are released
// before we try to acquire our lock // before we try to acquire our lock
// 300 seconds = 5 minutes timeout // 300 seconds = 5 minutes timeout
$lockingMngr->releaseAgedLocks(300); $lockingMngr->releaseAgedLocks(300);
// Try to get the lock on a record // Try to get the lock on a record
$gotLock = $lockingMngr->getLock( $gotLock = $lockingMngr->getLock(
// The record to lock. This can be any Doctrine_Record // The record to lock. This can be any Doctrine_Record
$myRecordToLock, $myRecordToLock,
// The unique identifier of the user who is trying to get the lock // The unique identifier of the user who is trying to get the lock
'Bart Simpson' 'Bart Simpson'
); );
if($gotLock) if($gotLock)
{ {
echo "Got lock!"; echo "Got lock!";
// ... proceed // ... proceed
} }
else else
{ {
echo "Sorry, someone else is currently working on this record"; echo "Sorry, someone else is currently working on this record";
} }
} }
catch(Doctrine_Locking_Exception $dle) catch(Doctrine_Locking_Exception $dle)
{ {
echo $dle->getMessage(); echo $dle->getMessage();
// handle the error // handle the error
} }
</code> </code>
At the page where the transaction finishes... At the page where the transaction finishes...
<code type="php"> <code type="php">
// Get a locking manager instance // Get a locking manager instance
$lockingMngr = new Doctrine_Locking_Manager_Pessimistic(); $lockingMngr = new Doctrine_Locking_Manager_Pessimistic();
try try
{ {
if($lockingMngr->releaseLock($myRecordToUnlock, 'Bart Simpson')) if($lockingMngr->releaseLock($myRecordToUnlock, 'Bart Simpson'))
{ {
echo "Lock released"; echo "Lock released";
} }
else else
{ {
echo "Record was not locked. No locks released."; echo "Record was not locked. No locks released.";
} }
} }
catch(Doctrine_Locking_Exception $dle) catch(Doctrine_Locking_Exception $dle)
{ {
echo $dle->getMessage(); echo $dle->getMessage();
// handle the error // handle the error
} }
</code> </code>
[<b>Note</b>: The term 'Transaction' doesnt refer to database transactions here but to the general meaning of this term]<br /> [**Note**: The term 'Transaction' doesnt refer to database transactions here but to the general meaning of this term]
[<b>Note</b>: This component is in <b>Alpha State</b>]<br />
<br /> [**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 /> Locking is a mechanism to control concurrency. The two most well known locking strategies
<b>Optimistic Locking:</b><br /> are optimistic and pessimistic locking. The following is a short description of these
The state/version of the object(s) is noted when the transaction begins. two strategies from which only pessimistic locking is currently supported by Doctrine.
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 **Optimistic Locking:**
will participate in transactions on the same objects at the same time.<br />
<br /> The state/version of the object(s) is noted when the transaction begins.
<b>Pessimistic Locking:</b><br /> When the transaction finishes the noted state/version of the participating objects is compared
The objects that need to participate in the transaction are locked at the moment to the current state/version. When the states/versions differ the objects have been modified
the user starts the transaction. No other user can start a transaction that operates on these objects by another transaction and the current transaction should fail.
while the locks are active. This ensures that the user who starts the transaction can be sure that This approach is called 'optimistic' because it is assumed that it is unlikely that several users
noone else modifies the same objects until he has finished his work.<br /> will participate in transactions on the same objects at the same time.
<br />
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.
**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.
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. Don't hesitate to contact me if you have questions, ideas, ect.
- Possibility to release locks of a specific Record type (i.e. releasing all locks on 'User' - Possibility to release locks of a specific Record type (i.e. releasing all locks on 'User'
objects). objects).
The pessimistic offline locking manager stores the locks in the database (therefore 'offline'). The pessimistic offline locking manager stores the locks in the database (therefore 'offline').
The required locking table is automatically created when you try to instantiate an instance The required locking table is automatically created when you try to instantiate an instance
of the manager and the ATTR_CREATE_TABLES is set to TRUE. of the manager and the ATTR_CREATE_TABLES is set to TRUE.
This behaviour may change in the future to provide a centralised and consistent table creation This behaviour may change in the future to provide a centralised and consistent table creation
procedure for installation purposes. procedure for installation purposes.
...@@ -3,21 +3,27 @@ You can think of this validation as a gateway that needs to be passed right befo ...@@ -3,21 +3,27 @@ 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 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). 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. 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 /> This is done through the Doctrine_Manager (see the code below).
<br />
Once you enabled validation, you'll get a bunch of validations automatically:<br />
<br />
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 - 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 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 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" 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 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 - 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. sure that the value does not exceed the maximum length.
<code type="php"> <code type="php">
// turning on validation // turning on validation
Doctrine_Manager::getInstance()->setAttribute(Doctrine::ATTR_VLD, true); Doctrine_Manager::getInstance()->setAttribute(Doctrine::ATTR_VLD, true);
</code> </code>
Here is a list of predefined validators. You cannot use these names for your custom validators. Here is a list of predefined validators. You cannot use these names for your custom validators.
|| **name** || **arguments** || **task** || || **name** || **arguments** || **task** ||
|| email || || Check if value is valid email.|| || email || || Check if value is valid email.||
|| notblank || || Check if value is not blank.|| || notblank || || Check if value is not blank.||
|| notnull || || Check if value is not null.|| || notnull || || Check if value is not null.||
|| country || || Check if valid is valid country code.|| || country || || Check if valid is valid country code.||
|| ip || || Checks if value is valid IP (internet protocol) address.|| || ip || || Checks if value is valid IP (internet protocol) address.||
|| htmlcolor || || Checks if value is valid html color.|| || htmlcolor || || Checks if value is valid html color.||
|| nospace || || Check if value has no space chars. || || nospace || || Check if value has no space chars. ||
|| range || [min,max] || Checks if value is in range specified by arguments.|| || range || [min,max] || Checks if value is in range specified by arguments.||
|| unique || || Checks if value is unique in its database table. || || unique || || Checks if value is unique in its database table. ||
|| regexp || [expression] || Check if valie matches a given regexp. || || regexp || [expression] || Check if valie matches a given regexp. ||
The type and length validations are handy but most of the time they're not enough. Therefore 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 /> Doctrine provides some mechanisms that can be used to validate your data in more detail.
<br />
Validators: Validators are an easy way to specify further validations. Doctrine has a lot of predefined 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 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 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. 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 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 /> a predefined validator you have three options:
<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 /> - You can write the validator on your own.
<br />
- 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 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 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 /> a new validator. However if the validation is special it is better to use hooks provided by Doctrine:
<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 /> - validate() (Executed every time the record gets validated)
<br />
- 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 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). 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 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. 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 The following code snippet shows an example of how to define validators together with custom
validation:<br /> validation:
<code type="php"> <code type="php">
class User extends Doctrine_Record { 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 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 /> deal with these rules in the rest of your application.
<br />
Implicit validation:<br />
Implicit validation:
Whenever a record is going to be saved to the persistent data store (i.e. through calling $record->save()) 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 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 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 ...@@ -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. 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(). 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 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 /> provides an easy to use interface to inspect the errors.
<br />
Explicit validation:<br />
Explicit validation:
You can explicitly trigger the validation for any record at any time. For this purpose Doctrine_Record 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 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 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 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 /> the error stack of the record that didnt pass validation through Doctrine_Record::getErrorStack().
<br />
The following code snippet shows an example of handling implicit validation which caused a Doctrine_Validator_Exception. The following code snippet shows an example of handling implicit validation which caused a Doctrine_Validator_Exception.
<code type="php"> <code type="php">
......
Database views can greatly increase the performance of complex queries. You can think of them as Database views can greatly increase the performance of complex queries. You can think of them as
cached queries. Doctrine_View provides integration between database views and DQL queries. cached queries. Doctrine_View provides integration between database views and DQL queries.
<code type="php"> <code type="php">
$conn = Doctrine_Manager::getInstance() $conn = Doctrine_Manager::getInstance()
->openConnection(new PDO("dsn","username","password")); ->openConnection(new PDO("dsn","username","password"));
$query = new Doctrine_Query($conn); $query = new Doctrine_Query($conn);
$query->from('User.Phonenumber')->limit(20); $query->from('User.Phonenumber')->limit(20);
$view = new Doctrine_View($query, 'MyView'); $view = new Doctrine_View($query, 'MyView');
// creating a database view // creating a database view
$view->create(); $view->create();
// dropping the view from the database // dropping the view from the database
$view->drop(); $view->drop();
</code> </code>
<code type="php"> <code type="php">
$conn = Doctrine_Manager::getInstance() $conn = Doctrine_Manager::getInstance()
->openConnection(new PDO("dsn","username","password")); ->openConnection(new PDO("dsn","username","password"));
$query = new Doctrine_Query($conn); $query = new Doctrine_Query($conn);
$query->from('User.Phonenumber')->limit(20); $query->from('User.Phonenumber')->limit(20);
// hook the query into appropriate view // hook the query into appropriate view
$view = new Doctrine_View($query, 'MyView'); $view = new Doctrine_View($query, 'MyView');
// now fetch the data from the view // now fetch the data from the view
$coll = $view->execute(); $coll = $view->execute();
</code> </code>
There are couple of availible Cache attributes on Doctrine: There are couple of availible Cache attributes on Doctrine:
<ul>
<li \>Doctrine::ATTR_CACHE_SIZE * Doctrine::ATTR_CACHE_SIZE
<ul>
<li \> Defines which cache container Doctrine uses * Defines which cache container Doctrine uses
<li \> Possible values: Doctrine::CACHE_* (for example Doctrine::CACHE_FILE) * Possible values: Doctrine::CACHE_* (for example Doctrine::CACHE_FILE)
</ul>
<li \>Doctrine::ATTR_CACHE_DIR * Doctrine::ATTR_CACHE_DIR
<ul>
<li \> cache directory where .cache files are saved * cache directory where .cache files are saved
<li \> the default cache dir is %ROOT%/cachedir, where * the default cache dir is %ROOT%/cachedir, where
%ROOT% is automatically converted to doctrine root dir %ROOT% is automatically converted to doctrine root dir
</ul>
<li \>Doctrine::ATTR_CACHE_SLAM * 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 * 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 * Doctrine::ATTR_CACHE_SIZE
<ul>
<li \> Cache size attribute * Cache size attribute
</ul>
<li \>Doctrine::ATTR_CACHE_TTL * Doctrine::ATTR_CACHE_TTL
<ul>
<li \> How often the cache is cleaned * How often the cache is cleaned
</ul>
</ul>
Doctrine has very comprehensive and fast caching solution. 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: 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. 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. 2. When retrieving records from the database doctrine always tries to hit the cache first.
<br /><br />
4. When a Data Access Object is deleted or updated it will be deleted from the cache
</td></tr>
</table> 3. If a record (Doctrine_Record) is retrieved from database or inserted into database it will be saved into cache.
<br /><br />
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 /> 4. When a Data Access Object is deleted or updated it will be deleted from the cache ||
<table border=1 class='dashed' cellpadding=0 cellspacing=0>
<tr><td>
Now one might wonder that this kind of solution won't work since eventually the cache will be a copy of database!
1. Every time a cache file is accessed the id of that record will be added into the $fetched property of Doctrine_Cache So doctrine does the following things to ensure the cache won't get too big:
<br /><br />
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 || 1. Every time a cache file is accessed the id of that record will be added into the $fetched property of Doctrine_Cache
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. 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
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.
</td></tr> 3. Doctrine does propabalistic cache cleaning. The default interval is 200 page loads (= 200 constructed Doctrine_Managers). Basically this means
</table> that the average number of page loads between cache cleans is 200.
<br /><br />
So for every 199 fast page loads there is one page load which suffers a little overhead from the cache cleaning operation.
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. ||
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: 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. Option that defines the propability of which
<br \><br \> a query is getting cached.
<li \> cleanPropability <br \>
Option that defines the propability the actual cleaning will occur
when calling Doctrine_Cache::clean();
<br \><br \> * cleanPropability
<li \> statsPropability
<ul \> Option that defines the propability the actual cleaning will occur
when calling Doctrine_Cache::clean();
* statsPropability
<ul \>
<?php ?> <?php ?>
Doctrine_Cache offers an intuitive and easy-to-use query caching solution. It provides the following things: 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) * 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
full freedom of whether or not he/she wants to take advantage of this feature.
<br \><br \> * 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.
<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> * Advanced options for fine-tuning. Doctrine_Cache has many options for fine-tuning performance.
<br \><br \>
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. * Cache hooks itself directly into Doctrine_Db eventlistener system allowing it to be easily added on-demand.
<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 \> 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 \>
Initializing a new cache instance:
<br \><br \>
<?php Now eventually the cache would grow very big, hence Doctrine uses propabalistic cache cleaning.
renderCode("<?php When calling Doctrine_Cache::clean() with cleanPropability = 0.25 there is a 25% chance of the clean operation being invoked.
\$dbh = new Doctrine_Db('mysql:host=localhost;dbname=test', \$user, \$pass); 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.
\$cache = new Doctrine_Cache('memcache');
// register it as a Doctrine_Db listener
\$dbh->addListener(\$cache);
?>");
?>
<br \><br \>
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. Initializing a new cache instance:
<br \><br \>
<code type="php">
\$dbh = new Doctrine_Db('mysql:host=localhost;dbname=test', \$user, \$pass);
\$cache = new Doctrine_Cache('memcache');
// register it as a Doctrine_Db listener
\$dbh->addListener(\$cache);
?></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.
<ul>
<li \>Negative numbers are not permitted as indices. * 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. * 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. * 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. * 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: * 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>
<code type="php"> <code type="php">
$sampleArray = array('Doctrine', 'ORM', 1, 2, 3); $sampleArray = array('Doctrine', 'ORM', 1, 2, 3);
$sampleArray = array(1, 2, 3, $sampleArray = array(1, 2, 3,
$a, $b, $c, $a, $b, $c,
56.44, $d, 500); 56.44, $d, 500);
$sampleArray = array('first' => 'firstValue', $sampleArray = array('first' => 'firstValue',
'second' => 'secondValue'); 'second' => 'secondValue');
<ul>
<li \>Classes must be named by following the naming conventions. * 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). * 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. * 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. * Any code within a class must be indented four spaces.
</ul>
<ul>
<li \>Only one class is permitted per PHP file. * Only one class is permitted per PHP file.
</ul>
<ul>
<li \>Placing additional code in a class file is NOT permitted. * Placing additional code in a class file is NOT permitted.
</ul>
This is an example of an acceptable class declaration: This is an example of an acceptable class declaration:
<code type="php"> <code type="php">
/** /**
* Documentation here * Documentation here
*/ */
class Doctrine_SampleClass { class Doctrine_SampleClass {
// entire content of class // entire content of class
// must be indented four spaces // must be indented four spaces
} }
<?php ?> <?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. * 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. * 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. * 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 <code type="php">
renderCode("<?php if (\$foo != 2) {
if (\$foo != 2) { \$foo = 2;
\$foo = 2; }</code>
}");
?> * For "if" statements that include "elseif" or "else", the formatting must be as in these examples:
<ul>
<li \>For "if" statements that include "elseif" or "else", the formatting must be as in these examples: <code type="php">
</ul> if (\$foo != 1) {
<?php \$foo = 1;
renderCode("<?php } else {
if (\$foo != 1) { \$foo = 3;
\$foo = 1; }
} else { if (\$foo != 2) {
\$foo = 3; \$foo = 2;
} } elseif (\$foo == 1) {
if (\$foo != 2) { \$foo = 3;
\$foo = 2; } else {
} elseif (\$foo == 1) { \$foo = 11;
\$foo = 3; }</code>
} else {
\$foo = 11;
}"); * 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">
<ul> if (\$foo != 1)
<li \>PHP allows for these statements to be written without braces in some circumstances, the following format for if statements is also allowed: \$foo = 1;
</ul> else
<?php \$foo = 3;
renderCode("<?php
if (\$foo != 1) if (\$foo != 2)
\$foo = 1; \$foo = 2;
else elseif (\$foo == 1)
\$foo = 3; \$foo = 3;
else
if (\$foo != 2) \$foo = 11;
\$foo = 2; </code>
elseif (\$foo == 1)
\$foo = 3; * 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.
else
\$foo = 11;
"); * 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> <code type="php">
<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. switch (\$case) {
</ul> case 1:
<ul> case 2:
<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. break;
</ul> case 3:
<?php break;
renderCode("<?php default:
switch (\$case) { break;
case 1: }
case 2: ?></code>
break;
case 3: * The construct default may never be omitted from a switch statement.
break;
default:
break;
}
?>");
?>
<ul>
<li \>The construct default may never be omitted from a switch statement.
</ul>
<?php ?> <?php ?>
* Methods must be named by following the naming conventions. * Methods must be named by following the naming conventions.
* Methods must always declare their visibility by using one of the private, protected, or public constructs. * Methods must always declare their visibility by using one of the private, protected, or public constructs.
* Like classes, the brace is always written right after the method name. There is no space between the function name and the opening parenthesis for the arguments. * Like classes, the brace is always written right after the method name. There is no space between the function name and the opening parenthesis for the arguments.
* Functions in the global scope are strongly discouraged. * Functions in the global scope are strongly discouraged.
* This is an example of an acceptable function declaration in a class: * This is an example of an acceptable function declaration in a class:
<code type="php"> <code type="php">
/** /**
* Documentation Block Here * Documentation Block Here
*/ */
class Foo { class Foo {
/** /**
* Documentation Block Here * Documentation Block Here
*/ */
public function bar() { public function bar() {
// entire content of function // entire content of function
// must be indented four spaces // must be indented four spaces
} }
}</code> }</code>
* Passing by-reference is permitted in the function declaration only: * Passing by-reference is permitted in the function declaration only:
<code type="php"> <code type="php">
/** /**
* Documentation Block Here * Documentation Block Here
*/ */
class Foo { class Foo {
/** /**
* Documentation Block Here * Documentation Block Here
*/ */
public function bar(&\$baz) { public function bar(&\$baz) {
} }
} }
</code> </code>
* Call-time pass by-reference is prohibited. * Call-time pass by-reference is prohibited.
* The return value must not be enclosed in parentheses. This can hinder readability and can also break code if a method is later changed to return by reference. * The return value must not be enclosed in parentheses. This can hinder readability and can also break code if a method is later changed to return by reference.
<code type="php"> <code type="php">
/** /**
* Documentation Block Here * Documentation Block Here
*/ */
class Foo { class Foo {
/** /**
* WRONG * WRONG
*/ */
public function bar() { public function bar() {
return(\$this->bar); return(\$this->bar);
} }
/** /**
* RIGHT * RIGHT
*/ */
public function bar() { public function bar() {
return \$this->bar; return \$this->bar;
} }
}</code> }</code>
* Function arguments are separated by a single trailing space after the comma delimiter. This is an example of an acceptable function call for a function that takes three arguments: * Function arguments are separated by a single trailing space after the comma delimiter. This is an example of an acceptable function call for a function that takes three arguments:
<code type="php"> <code type="php">
threeArguments(1, 2, 3); threeArguments(1, 2, 3);
?></code> ?></code>
* Call-time pass by-reference is prohibited. See the function declarations section for the proper way to pass function arguments by-reference. * Call-time pass by-reference is prohibited. See the function declarations section for the proper way to pass function arguments by-reference.
* For functions whose arguments permitted arrays, the function call may include the "array" construct and can be split into multiple lines to improve readability. In these cases, the standards for writing arrays still apply: * For functions whose arguments permitted arrays, the function call may include the "array" construct and can be split into multiple lines to improve readability. In these cases, the standards for writing arrays still apply:
<code type="php"> <code type="php">
threeArguments(array(1, 2, 3), 2, 3); threeArguments(array(1, 2, 3), 2, 3);
threeArguments(array(1, 2, 3, 'Framework', threeArguments(array(1, 2, 3, 'Framework',
'Doctrine', 56.44, 500), 2, 3); 'Doctrine', 56.44, 500), 2, 3);
?></code> ?></code>
Documentation Format 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/ * 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>
Methods: Methods:
<ul>
<li \>Every method, must have a docblock that contains at a minimum: * Every method, must have a docblock that contains at a minimum:
</ul>
<ul>
<li \>A description of the function * A description of the function
</ul>
<ul>
<li \>All of the arguments * All of the arguments
</ul>
<ul>
<li \>All of the possible return values * All of the possible return values
</ul>
<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. * 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: * If a function/method may throw an exception, use @throws:
</ul>
<ul>
<li \>@throws exceptionclass [description] * @throws exceptionclass [description]
</ul>
PHP code must always be delimited by the full-form, standard PHP tags (<?php ?>) PHP code must always be delimited by the full-form, standard PHP tags (<?php ?>)
Short tags are never allowed. For files containing only PHP code, the closing tag must always be omitted Short tags are never allowed. For files containing only PHP code, the closing tag must always be omitted
<ul>
<li \>When a string is literal (contains no variable substitutions), the apostrophe or "single quote" must always used to demarcate the string: * 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: * 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: * 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: * 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: * 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>
<code type="php"> <code type="php">
// literal string // literal string
$string = 'something'; $string = 'something';
// string contains apostrophes // string contains apostrophes
$sql = "SELECT id, name FROM people WHERE name = 'Fred' OR name = 'Susan'"; $sql = "SELECT id, name FROM people WHERE name = 'Fred' OR name = 'Susan'";
// variable substitution // variable substitution
$greeting = "Hello $name, welcome back!"; $greeting = "Hello $name, welcome back!";
// concatenation // concatenation
$framework = 'Doctrine' . ' ORM ' . 'Framework'; $framework = 'Doctrine' . ' ORM ' . 'Framework';
// concatenation line breaking // concatenation line breaking
$sql = "SELECT id, name FROM user " $sql = "SELECT id, name FROM user "
. "WHERE name = ? " . "WHERE name = ? "
. "ORDER BY name ASC"; . "ORDER BY name ASC";
<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, 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. 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". 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. 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: Following rules must apply to all constants used within Doctrine framework:
<ul><li \> Constants may contain both alphanumeric characters and the underscore.</ul>
<ul><li \> Constants must always have all letters capitalized.</ul>
<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"> * Constants may contain both alphanumeric characters and the underscore.
class Doctrine_SomeClass {
const MY_CONSTANT = 'something'; * Constants must always have all letters capitalized.
}
print Doctrine_SomeClass::MY_CONSTANT; * 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.
</code>
* Constants must be defined as class members by using the "const" construct. Defining constants in the global scope with "define" is NOT permitted.
<code type="php">
class Doctrine_SomeClass {
const MY_CONSTANT = 'something';
}
print Doctrine_SomeClass::MY_CONSTANT;
</code>
<ul>
<li \>For all other files, only alphanumeric characters, underscores, and the dash character ("-") are permitted. Spaces are prohibited. * 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: * 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 \> Doctrine/Db.php
</ul>
<ul>
<li \>File names must follow the mapping to class names described above.
</ul> 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. <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. <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. <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 <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. for this is that since all user defined ActiveRecords inherit Doctrine_Record, it should populate the get / set namespace as little as possible.
</ul>
<ul>
<li>Functions in the global scope ("floating functions") are NOT permmitted. All static functions should be wrapped in a static class. <li>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: (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 \> Doctrine_Db_EventListener_Interface
</ul>
Doctrine_EventListener_Interface
All variables must satisfy the following conditions: 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. * 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. * Variable names must always start with a lowercase letter and follow the "camelCaps" capitalization convention.
</ul>
<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. * 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: * Within the framework certain generic object variables should always use the following names:
<ul>
<li \> Doctrine_Connection -> <i>$conn</i> * Doctrine_Connection -> //$conn//
<li \> Doctrine_Collection -> <i>$coll</i> * Doctrine_Collection -> //$coll//
<li \> Doctrine_Manager -> <i>$manager</i> * Doctrine_Manager -> //$manager//
<li \> Doctrine_Query -> <i>$query</i> * Doctrine_Query -> //$query//
<li \> Doctrine_Db -> <i>$db</i> * 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), 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. 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. 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.
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.
Use an indent of 4 spaces, with no tabs. Use an indent of 4 spaces, with no tabs.
<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. * 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). * 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). * Do not use the carriage return/linefeed combination (CRLF) as Windows computers (0x0D, 0x0A).
</ul>
The target line length is 80 characters, i.e. developers should aim keep code as close to the 80-column boundary as is practical. However, longer lines are acceptable. The maximum length of any line of PHP code is 120 characters. The target line length is 80 characters, i.e. developers should aim keep code as close to the 80-column boundary as is practical. However, longer lines are acceptable. The maximum length of any line of PHP code is 120 characters.
<b>CLASSES</b><br \><br \> **CLASSES**
<ul>
<li \>
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 All test classes should be referring to a class or specific testing aspect of some class.
<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
of the Doctrine_Record class. For example //Doctrine_Record_TestCase// is a valid name since its referring to class named
<br \><br \> //Doctrine_Record//.
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 //Doctrine_Record_State_TestCase// is also a valid name since its referring to testing the state aspect
</ul> of the Doctrine_Record class.
<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. However something like //Doctrine_PrimaryKey_TestCase// is not valid since its way too generic.
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
what it is testing. Also we can run agile documentation tool to get little up-to-date system information.
<br \><br \> * Every class should have atleast one TestCase equivalent
NOTE: Commonly used testing method naming convention TestCase::test[methodName] is *NOT* allowed in Doctrine. So in this case * All testcase classes should inherit Doctrine_UnitTestCase
<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
divide the method into smaller methods.
</ul> **METHODS**
<b>ASSERTIONS</b><br \><br \>
<ul>
<li \>There should never be assertions within any loops and rarely within functions.
* 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.
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()** 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.
**ASSERTIONS**
* There should never be assertions within any loops and rarely within functions.
<code type="php"> <code type="php">
class Customer extends Doctrine_Record { class Customer extends Doctrine_Record {
public function setUp() { public function setUp() {
// setup code goes here // setup code goes here
} }
public function setTableDefinition() { public function setTableDefinition() {
// table definition code goes here // table definition code goes here
} }
public function getAvailibleProducts() { public function getAvailibleProducts() {
// some code // some code
} }
public function setName($name) { public function setName($name) {
if($this->isValidName($name)) if($this->isValidName($name))
$this->set("name",$name); $this->set("name",$name);
} }
public function getName() { public function getName() {
return $this->get("name"); return $this->get("name");
} }
} }
</code> </code>
<code type="php"> <code type="php">
// custom primary key column name // custom primary key column name
class Group extends Doctrine_Record { class Group extends Doctrine_Record {
public function setUp() { public function setUp() {
$this->setPrimaryKeyColumn("group_id"); $this->setPrimaryKeyColumn("group_id");
} }
} }
</code> </code>
<code type="php"> <code type="php">
$manager = Doctrine_Manager::getInstance(); $manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_LISTENER, new MyListener()); $manager->setAttribute(Doctrine::ATTR_LISTENER, new MyListener());
</code> </code>
Doctrine has a three-level configuration structure. You can set configuration attributes in global, connection and table level. Doctrine has a three-level configuration structure. You can set configuration attributes in global, connection and table level.
If the same attribute is set on both lower level and upper level, the uppermost attribute will always be used. So for example If the same attribute is set on both lower level and upper level, the uppermost attribute will always be used. So for example
if user first sets default fetchmode in global level to Doctrine::FETCH_BATCH and then sets 'example' table fetchmode to Doctrine::FETCH_LAZY, 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. 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. <li> Global level
</ul>
<li> Connection level The attributes set in global level will affect every connection and every table in each connection.
<ul>
The attributes set in connection level will take effect on each table in that connection. <li> Connection level
</ul>
<li> Table level The attributes set in connection level will take effect on each table in that connection.
<ul>
The attributes set in table level will take effect only on that table. <li> Table level
</ul>
The attributes set in table level will take effect only on that table.
<code type="php">
// setting a global level attribute
$manager = Doctrine_Manager::getInstance(); <code type="php">
// setting a global level attribute
$manager->setAttribute(Doctrine::ATTR_VLD, false); $manager = Doctrine_Manager::getInstance();
// setting a connection level attribute $manager->setAttribute(Doctrine::ATTR_VLD, false);
// (overrides the global level attribute on this connection)
// setting a connection level attribute
$conn = $manager->openConnection(new PDO('dsn', 'username', 'pw')); // (overrides the global level attribute on this connection)
$conn->setAttribute(Doctrine::ATTR_VLD, true); $conn = $manager->openConnection(new PDO('dsn', 'username', 'pw'));
// setting a table level attribute $conn->setAttribute(Doctrine::ATTR_VLD, true);
// (overrides the connection/global level attribute on this table)
// setting a table level attribute
$table = $conn->getTable('User'); // (overrides the connection/global level attribute on this table)
$table->setAttribute(Doctrine::ATTR_LISTENER, new UserListener()); $table = $conn->getTable('User');
</code>
$table->setAttribute(Doctrine::ATTR_LISTENER, new UserListener());
</code>
<li> Doctrine::ATTR_LISTENER <li> Doctrine::ATTR_LISTENER
<li> Doctrine::ATTR_FETCHMODE = 2; <li> Doctrine::ATTR_FETCHMODE = 2;
<li> Doctrine::ATTR_CACHE_DIR = 3; <li> Doctrine::ATTR_CACHE_DIR = 3;
<li> Doctrine::ATTR_CACHE_TTL = 4; <li> Doctrine::ATTR_CACHE_TTL = 4;
<li> Doctrine::ATTR_CACHE_SIZE = 5; <li> Doctrine::ATTR_CACHE_SIZE = 5;
<li> Doctrine::ATTR_CACHE_SLAM = 6; <li> Doctrine::ATTR_CACHE_SLAM = 6;
<li> Doctrine::ATTR_CACHE = 7; <li> Doctrine::ATTR_CACHE = 7;
<li> Doctrine::ATTR_BATCH_SIZE = 8; <li> Doctrine::ATTR_BATCH_SIZE = 8;
<li> Doctrine::ATTR_PK_COLUMNS = 9; <li> Doctrine::ATTR_PK_COLUMNS = 9;
/** /**
* primary key type attribute * primary key type attribute
*/ */
<li> Doctrine::ATTR_PK_TYPE = 10; <li> Doctrine::ATTR_PK_TYPE = 10;
/** /**
* locking attribute * locking attribute
*/ */
<li> Doctrine::ATTR_LOCKMODE = 11; <li> Doctrine::ATTR_LOCKMODE = 11;
/** /**
* validatate attribute * validatate attribute
*/ */
<li> Doctrine::ATTR_VLD = 12; <li> Doctrine::ATTR_VLD = 12;
/** /**
* name prefix attribute * name prefix attribute
*/ */
<li> Doctrine::ATTR_NAME_PREFIX = 13; <li> Doctrine::ATTR_NAME_PREFIX = 13;
/** /**
* create tables attribute * create tables attribute
*/ */
<li> Doctrine::ATTR_CREATE_TABLES = 14; <li> Doctrine::ATTR_CREATE_TABLES = 14;
/** /**
* collection key attribute * collection key attribute
*/ */
<li> Doctrine::ATTR_COLL_KEY = 15; <li> Doctrine::ATTR_COLL_KEY = 15;
/** /**
* collection limit attribute * collection limit attribute
*/ */
<li> Doctrine::ATTR_COLL_LIMIT = 16; <li> Doctrine::ATTR_COLL_LIMIT = 16;
<code type="php"> <code type="php">
// setting default batch size for batch collections // setting default batch size for batch collections
$manager->setAttribute(Doctrine::ATTR_BATCH_SIZE, 7); $manager->setAttribute(Doctrine::ATTR_BATCH_SIZE, 7);
</code> </code>
<code type="php"> <code type="php">
// setting default event listener // setting default event listener
$manager->setAttribute(Doctrine::ATTR_LISTENER, new MyListener()); $manager->setAttribute(Doctrine::ATTR_LISTENER, new MyListener());
</code> </code>
<code type="php"> <code type="php">
// sets the default collection type (fetching strategy) // sets the default collection type (fetching strategy)
$manager->setAttribute(Doctrine::ATTR_FETCHMODE, Doctrine::FETCH_LAZY); $manager->setAttribute(Doctrine::ATTR_FETCHMODE, Doctrine::FETCH_LAZY);
</code> </code>
<?php ?> <?php ?>
You can quote the db identifiers (table and field names) with quoteIdentifier(). The delimiting style depends on which database driver is being used. NOTE: just because you CAN use delimited identifiers, it doesn't mean you SHOULD use them. In general, they end up causing way more problems than they solve. Anyway, it may be necessary when you have a reserved word as a field name (in this case, we suggest you to change it, if you can). You can quote the db identifiers (table and field names) with quoteIdentifier(). The delimiting style depends on which database driver is being used. NOTE: just because you CAN use delimited identifiers, it doesn't mean you SHOULD use them. In general, they end up causing way more problems than they solve. Anyway, it may be necessary when you have a reserved word as a field name (in this case, we suggest you to change it, if you can).
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. 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: 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 * backtick (`) -- due to MySQL
<li \>brackets ([ or ]) -- due to Access * double quote (") -- due to Oracle
</ul>
* brackets ([ or ]) -- due to Access
Delimited identifiers are known to generally work correctly under the following drivers:
<br \>
<ul> Delimited identifiers are known to generally work correctly under the following drivers:
<li \>Mssql
<li \>Mysql
* Mssql
<li \>Oracle
* Mysql
<li \>Pgsql
* Oracle
<li \>Sqlite
* Pgsql
<li \>Firebird
</ul> * Sqlite
* Firebird
When using the quoteIdentifiers option, all of the field identifiers will be automatically quoted in the resulting SQL statements:
<br \><br \>
<?php
renderCode("<?php When using the quoteIdentifiers option, all of the field identifiers will be automatically quoted in the resulting SQL statements:
\$conn->setAttribute('quote_identifiers', true);
?>");
?>
<br \><br \> <code type="php">
\$conn->setAttribute('quote_identifiers', true);
?></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 \> will result in a SQL statement that all the field names are quoted with the backtick '`' operator (in MySQL).
<div class='sql'>SELECT * FROM sometable WHERE id='123'</div>
<div class='sql'>SELECT * FROM `sometable` WHERE `id` = '123'</div>
as opposed to:
<div class='sql'>SELECT * FROM sometable WHERE id='123'</div>
<code type="php"> <code type="php">
// sets the default offset collection limit // sets the default offset collection limit
$manager->setAttribute(Doctrine::ATTR_COLL_LIMIT, 10); $manager->setAttribute(Doctrine::ATTR_COLL_LIMIT, 10);
</code> </code>
<?php ?> <?php ?>
Each database management system (DBMS) has it's own behaviors. For example, some databases capitalize field names in their output, some lowercase them, while others leave them alone. These quirks make it difficult to port your scripts over to another server type. PEAR Doctrine:: strives to overcome these differences so your program can switch between DBMS's without any changes. Each database management system (DBMS) has it's own behaviors. For example, some databases capitalize field names in their output, some lowercase them, while others leave them alone. These quirks make it difficult to port your scripts over to another server type. PEAR Doctrine:: strives to overcome these differences so your program can switch between DBMS's without any changes.
You control which portability modes are enabled by using the portability configuration option. Configuration options are set via factory() and setOption(). 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. 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 \>
Portability Mode Constants
<i>Doctrine::PORTABILITY_ALL (default)</i>
<br \><br \>
turn on all portability features. this is the default setting.
<br \><br \>
<i>Doctrine::PORTABILITY_DELETE_COUNT</i> //Doctrine::PORTABILITY_ALL (default)//
<br \><br \>
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> turn on all portability features. this is the default setting.
<br \><br \>
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> //Doctrine::PORTABILITY_DELETE_COUNT//
<br \><br \>
Makes certain error messages in certain drivers compatible with those from other DBMS's
<br \><br \>
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.
Table 33-1. Error Code Re-mappings
Driver Description Old Constant New Constant
mysql, mysqli unique and primary key constraints Doctrine::ERROR_ALREADY_EXISTS Doctrine::ERROR_CONSTRAINT //Doctrine::PORTABILITY_EMPTY_TO_NULL//
mysql, mysqli not-null constraints Doctrine::ERROR_CONSTRAINT Doctrine::ERROR_CONSTRAINT_NOT_NULL
<br \><br \>
<i>Doctrine::PORTABILITY_FIX_ASSOC_FIELD_NAMES</i> 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 \>
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> //Doctrine::PORTABILITY_ERRORS//
<br \><br \>
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> Makes certain error messages in certain drivers compatible with those from other DBMS's
<br \><br \>
Turn off all portability features
<br \><br \>
<i>Doctrine::PORTABILITY_NUMROWS</i>
<br \><br \> Table 33-1. Error Code Re-mappings
Enable hack that makes numRows() work in Oracle
<br \><br \> Driver Description Old Constant New Constant
<i>Doctrine::PORTABILITY_RTRIM</i> mysql, mysqli unique and primary key constraints Doctrine::ERROR_ALREADY_EXISTS Doctrine::ERROR_CONSTRAINT
<br \><br \> mysql, mysqli not-null constraints Doctrine::ERROR_CONSTRAINT Doctrine::ERROR_CONSTRAINT_NOT_NULL
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 //Doctrine::PORTABILITY_FIX_ASSOC_FIELD_NAMES//
<br \><br \>
<?php
renderCode("<?php
\$conn->setAttribute('portability', 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.
Doctrine::PORTABILITY_FIX_CASE | Doctrine::PORTABILITY_RTRIM);
?>");
?> //Doctrine::PORTABILITY_FIX_CASE//
<br \><br \> 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
Using setAttribute() to enable all portability options except trimming
<br \><br \>
<?php //Doctrine::PORTABILITY_NONE//
renderCode("<?php
\$conn->setAttribute('portability',
Doctrine::PORTABILITY_ALL ^ Doctrine::PORTABILITY_RTRIM);
?>"); Turn off all portability features
?>
//Doctrine::PORTABILITY_NUMROWS//
Enable hack that makes numRows() work in Oracle
//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.
Using setAttribute() to enable portability for lowercasing and trimming
<code type="php">
\$conn->setAttribute('portability',
Doctrine::PORTABILITY_FIX_CASE | Doctrine::PORTABILITY_RTRIM);
?></code>
Using setAttribute() to enable all portability options except trimming
<code type="php">
\$conn->setAttribute('portability',
Doctrine::PORTABILITY_ALL ^ Doctrine::PORTABILITY_RTRIM);
?></code>
<code type="php"> <code type="php">
// setting default lockmode // setting default lockmode
$manager->setAttribute(Doctrine::ATTR_LOCKMODE, Doctrine::LOCK_PESSIMISTIC); $manager->setAttribute(Doctrine::ATTR_LOCKMODE, Doctrine::LOCK_PESSIMISTIC);
</code> </code>
<code type="php"> <code type="php">
// turns automatic table creation off // turns automatic table creation off
$manager->setAttribute(Doctrine::ATTR_CREATE_TABLES, false); $manager->setAttribute(Doctrine::ATTR_CREATE_TABLES, false);
</code> </code>
<code type="php"> <code type="php">
// turns transactional validation on // turns transactional validation on
$manager->setAttribute(Doctrine::ATTR_VLD, true); $manager->setAttribute(Doctrine::ATTR_VLD, true);
</code> </code>
<code type="php"> <code type="php">
class Email extends Doctrine_Record { class Email extends Doctrine_Record {
public function setUp() { public function setUp() {
$this->setAttribute(Doctrine::ATTR_LISTENER,new MyListener()); $this->setAttribute(Doctrine::ATTR_LISTENER,new MyListener());
} }
public function setTableDefinition() { public function setTableDefinition() {
$this->hasColumn("address","string",150,"email|unique"); $this->hasColumn("address","string",150,"email|unique");
} }
} }
</code> </code>
<code type="php"> <code type="php">
// setting default fetchmode // setting default fetchmode
// availible fetchmodes are Doctrine::FETCH_LAZY, Doctrine::FETCH_IMMEDIATE and Doctrine::FETCH_BATCH // availible fetchmodes are Doctrine::FETCH_LAZY, Doctrine::FETCH_IMMEDIATE and Doctrine::FETCH_BATCH
// the default fetchmode is Doctrine::FETCH_LAZY // the default fetchmode is Doctrine::FETCH_LAZY
class Address extends Doctrine_Record { class Address extends Doctrine_Record {
public function setUp() { public function setUp() {
$this->setAttribute(Doctrine::ATTR_FETCHMODE,Doctrine::FETCH_IMMEDIATE); $this->setAttribute(Doctrine::ATTR_FETCHMODE,Doctrine::FETCH_IMMEDIATE);
} }
} }
</code> </code>
<code type="php"> <code type="php">
// using sequences // using sequences
class User extends Doctrine_Record { class User extends Doctrine_Record {
public function setUp() { public function setUp() {
$this->setSequenceName("user_seq"); $this->setSequenceName("user_seq");
} }
} }
</code> </code>
<?php ?> <?php ?>
Doctrine allows you to bind connections to components (= your ActiveRecord classes). This means everytime a component issues a query 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. or data is being fetched from the table the component is pointing at Doctrine will use the bound connection.
<br \> <br \>
<?php
renderCode("<?php
\$conn = \$manager->openConnection(new PDO('dsn','username','password'), 'connection 1'); <code type="php">
\$conn = \$manager->openConnection(new PDO('dsn','username','password'), 'connection 1');
\$conn2 = \$manager->openConnection(new PDO('dsn2','username2','password2'), 'connection 2');
\$conn2 = \$manager->openConnection(new PDO('dsn2','username2','password2'), 'connection 2');
\$manager->bindComponent('User', 'connection 1');
\$manager->bindComponent('User', 'connection 1');
\$manager->bindComponent('Group', 'connection 2');
\$manager->bindComponent('Group', 'connection 2');
\$q = new Doctrine_Query();
\$q = new Doctrine_Query();
// Doctrine uses 'connection 1' for fetching here
\$users = \$q->from('User u')->where('u.id IN (1,2,3)')->execute(); // Doctrine uses 'connection 1' for fetching here
\$users = \$q->from('User u')->where('u.id IN (1,2,3)')->execute();
// Doctrine uses 'connection 2' for fetching here
\$groups = \$q->from('Group g')->where('g.id IN (1,2,3)')->execute(); // Doctrine uses 'connection 2' for fetching here
?>"); \$groups = \$q->from('Group g')->where('g.id IN (1,2,3)')->execute();
?> ?></code>
In order to connect to a database through Doctrine, you have to create a valid DSN - data source name. In order to connect to a database through Doctrine, you have to create a valid DSN - data source name.
Doctrine supports both PEAR DB/MDB2 like data source names as well as PDO style data source names. The following section deals with PEAR like data source names. If you need more info about the PDO-style data source names see http://www.php.net/manual/en/function.PDO-construct.php. Doctrine supports both PEAR DB/MDB2 like data source names as well as PDO style data source names. The following section deals with PEAR like data source names. If you need more info about the PDO-style data source names see http://www.php.net/manual/en/function.PDO-construct.php.
The DSN consists in the following parts: The DSN consists in the following parts:
**phptype**: Database backend used in PHP (i.e. mysql , pgsql etc.) **phptype**: Database backend used in PHP (i.e. mysql , pgsql etc.)
**dbsyntax**: Database used with regards to SQL syntax etc. **dbsyntax**: Database used with regards to SQL syntax etc.
**protocol**: Communication protocol to use ( i.e. tcp, unix etc.) **protocol**: Communication protocol to use ( i.e. tcp, unix etc.)
**hostspec**: Host specification (hostname[:port]) **hostspec**: Host specification (hostname[:port])
**database**: Database to use on the DBMS server **database**: Database to use on the DBMS server
**username**: User name for login **username**: User name for login
**password**: Password for login **password**: Password for login
**proto_opts**: Maybe used with protocol **proto_opts**: Maybe used with protocol
**option**: Additional connection options in URI query string format. options get separated by &. The Following table shows a non complete list of options: **option**: Additional connection options in URI query string format. options get separated by &. The Following table shows a non complete list of options:
**List of options** **List of options**
|| //Name// || //Description// || || //Name// || //Description// ||
|| charset || Some backends support setting the client charset.|| || charset || Some backends support setting the client charset.||
|| new_link || Some RDBMS do not create new connections when connecting to the same host multiple times. This option will attempt to force a new connection. || || new_link || Some RDBMS do not create new connections when connecting to the same host multiple times. This option will attempt to force a new connection. ||
The DSN can either be provided as an associative array or as a string. The string format of the supplied DSN is in its fullest form: The DSN can either be provided as an associative array or as a string. The string format of the supplied DSN is in its fullest form:
`` phptype(dbsyntax)://username:password@protocol+hostspec/database?option=value `` `` phptype(dbsyntax)://username:password@protocol+hostspec/database?option=value ``
Most variations are allowed: Most variations are allowed:
phptype://username:password@protocol+hostspec:110//usr/db_file.db phptype://username:password@protocol+hostspec:110//usr/db_file.db
phptype://username:password@hostspec/database phptype://username:password@hostspec/database
phptype://username:password@hostspec phptype://username:password@hostspec
phptype://username@hostspec phptype://username@hostspec
phptype://hostspec/database phptype://hostspec/database
phptype://hostspec phptype://hostspec
phptype:///database phptype:///database
phptype:///database?option=value&anotheroption=anothervalue phptype:///database?option=value&anotheroption=anothervalue
phptype(dbsyntax) phptype(dbsyntax)
phptype phptype
The currently supported database backends are: The currently supported database backends are:
||//fbsql//|| -> FrontBase || ||//fbsql//|| -> FrontBase ||
||//ibase//|| -> InterBase / Firebird (requires PHP 5) || ||//ibase//|| -> InterBase / Firebird (requires PHP 5) ||
||//mssql//|| -> Microsoft SQL Server (NOT for Sybase. Compile PHP --with-mssql) || ||//mssql//|| -> Microsoft SQL Server (NOT for Sybase. Compile PHP --with-mssql) ||
||//mysql//|| -> MySQL || ||//mysql//|| -> MySQL ||
||//mysqli//|| -> MySQL (supports new authentication protocol) (requires PHP 5) || ||//mysqli//|| -> MySQL (supports new authentication protocol) (requires PHP 5) ||
||//oci8 //|| -> Oracle 7/8/9/10 || ||//oci8 //|| -> Oracle 7/8/9/10 ||
||//pgsql//|| -> PostgreSQL || ||//pgsql//|| -> PostgreSQL ||
||//querysim//|| -> QuerySim || ||//querysim//|| -> QuerySim ||
||//sqlite//|| -> SQLite 2 || ||//sqlite//|| -> SQLite 2 ||
A second DSN format is supported phptype(syntax)://user:pass@protocol(proto_opts)/database A second DSN format is supported phptype(syntax)://user:pass@protocol(proto_opts)/database
If your database, option values, username or password contain characters used to delineate DSN parts, you can escape them via URI hex encodings: If your database, option values, username or password contain characters used to delineate DSN parts, you can escape them via URI hex encodings:
``: = %3a`` ``: = %3a``
``/ = %2f`` ``/ = %2f``
``@ = %40`` ``@ = %40``
``+ = %2b`` ``+ = %2b``
``( = %28`` ``( = %28``
``) = %29`` ``) = %29``
``? = %3f`` ``? = %3f``
``= = %3d`` ``= = %3d``
``& = %26`` ``& = %26``
Warning Warning
Please note, that some features may be not supported by all database backends. Please note, that some features may be not supported by all database backends.
Example Example
**Example 1.** Connect to database through a socket **Example 1.** Connect to database through a socket
mysql://user@unix(/path/to/socket)/pear mysql://user@unix(/path/to/socket)/pear
**Example 2.** Connect to database on a non standard port **Example 2.** Connect to database on a non standard port
pgsql://user:pass@tcp(localhost:5555)/pear pgsql://user:pass@tcp(localhost:5555)/pear
**Example 3.** Connect to SQLite on a Unix machine using options **Example 3.** Connect to SQLite on a Unix machine using options
sqlite:////full/unix/path/to/file.db?mode=0666 sqlite:////full/unix/path/to/file.db?mode=0666
**Example 4.** Connect to SQLite on a Windows machine using options **Example 4.** Connect to SQLite on a Windows machine using options
sqlite:///c:/full/windows/path/to/file.db?mode=0666 sqlite:///c:/full/windows/path/to/file.db?mode=0666
**Example 5.** Connect to MySQLi using SSL **Example 5.** Connect to MySQLi using SSL
mysqli://user:pass@localhost/pear?key=client-key.pem&cert=client-cert.pem mysqli://user:pass@localhost/pear?key=client-key.pem&cert=client-cert.pem
<?php ?> <?php ?>
Lazy-connecting to database is handled via Doctrine_Db wrapper. When using Doctrine_Db instead of PDO / Doctrine_Adapter, lazy-connecting 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).
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 \> This feature can be very useful
<?php when using for example page caching, hence not actually needing a database connection on every request. Remember connecting to database is an expensive operation.
renderCode("<?php
// we may use PDO / PEAR like DSN
// here we use PEAR like DSN
\$dbh = new Doctrine_Db('mysql://username:password@localhost/test'); <code type="php">
// !! no actual database connection yet !! // we may use PDO / PEAR like DSN
// here we use PEAR like DSN
// initalize a new Doctrine_Connection \$dbh = new Doctrine_Db('mysql://username:password@localhost/test');
\$conn = Doctrine_Manager::connection(\$dbh); // !! no actual database connection yet !!
// !! no actual database connection yet !!
// initalize a new Doctrine_Connection
// connects database and performs a query \$conn = Doctrine_Manager::connection(\$dbh);
\$conn->query('FROM User u'); // !! no actual database connection yet !!
?>"); // connects database and performs a query
?> \$conn->query('FROM User u');
?></code>
<?php ?> <?php ?>
From the start Doctrine has been designed to work with multiple connections. Unless separately specified Doctrine always uses the current connection 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 for executing the queries. The following example uses openConnection() second argument as an optional
connection alias. connection alias.
<br \><br \>
<?php
renderCode("<?php
// Doctrine_Manager controls all the connections <code type="php">
// Doctrine_Manager controls all the connections
\$manager = Doctrine_Manager::getInstance();
\$manager = Doctrine_Manager::getInstance();
// open first connection
// open first connection
\$conn = \$manager->openConnection(new PDO('dsn','username','password'), 'connection 1');
?> \$conn = \$manager->openConnection(new PDO('dsn','username','password'), 'connection 1');
"); ?>
?> </code>
<br \><br \>
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 \> For convenience Doctrine_Manager provides static method connection() which opens new connection when arguments are given to it and returns the current
<?php connection when no arguments have been speficied.
renderCode("<?php
// open first connection
\$conn = Doctrine_Manager::connection(new PDO('dsn','username','password'), 'connection 1'); <code type="php">
// open first connection
\$conn2 = Doctrine_Manager::connection();
\$conn = Doctrine_Manager::connection(new PDO('dsn','username','password'), 'connection 1');
// \$conn2 == \$conn
?> \$conn2 = Doctrine_Manager::connection();
");
?> // \$conn2 == \$conn
?>
</code>
<br \><br \>
The current connection is the lastly opened connection.
<br \><br \>
<?php
renderCode("<?php
// open second connection The current connection is the lastly opened connection.
\$conn2 = \$manager->openConnection(new PDO('dsn2','username2','password2'), 'connection 2');
\$manager->getCurrentConnection(); // \$conn2 <code type="php">
?>"); // open second connection
?>
<br \><br \> \$conn2 = \$manager->openConnection(new PDO('dsn2','username2','password2'), 'connection 2');
You can change the current connection by calling setCurrentConnection().
<br \><br \> \$manager->getCurrentConnection(); // \$conn2
<?php ?></code>
renderCode("<?php
\$manager->setCurrentConnection('connection 1');
\$manager->getCurrentConnection(); // \$conn You can change the current connection by calling setCurrentConnection().
?>
");
?>
<br \><br \> <code type="php">
You can iterate over the opened connection by simple passing the manager object to foreach clause. This is possible since Doctrine_Manager implements \$manager->setCurrentConnection('connection 1');
special IteratorAggregate interface.
<br \><br \> \$manager->getCurrentConnection(); // \$conn
<?php ?>
renderCode("<?php </code>
// iterating through connections
foreach(\$manager as \$conn) {
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.
?>");
?>
<code type="php">
// iterating through connections
foreach(\$manager as \$conn) {
}
?></code>
<?php ?> <?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: 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
\$dsn = 'mysql:dbname=testdb;host=127.0.0.1'; <code type="php">
\$user = 'dbuser'; \$dsn = 'mysql:dbname=testdb;host=127.0.0.1';
\$password = 'dbpass'; \$user = 'dbuser';
\$password = 'dbpass';
try {
\$dbh = new PDO(\$dsn, \$user, \$password); try {
} catch (PDOException \$e) { \$dbh = new PDO(\$dsn, \$user, \$password);
echo 'Connection failed: ' . \$e->getMessage(); } catch (PDOException \$e) {
} echo 'Connection failed: ' . \$e->getMessage();
?>"); }
?> ?></code>
<br \><br \>
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 If your database extension isn't supported by PDO you can use special Doctrine_Adapter class (if availible). The following example uses db2 adapter:
renderCode("<?php
\$dsn = 'db2:dbname=testdb;host=127.0.0.1';
\$user = 'dbuser';
\$password = 'dbpass'; <code type="php">
\$dsn = 'db2:dbname=testdb;host=127.0.0.1';
try { \$user = 'dbuser';
\$dbh = Doctrine_Adapter::connect(\$dsn, \$user, \$password); \$password = 'dbpass';
} catch (PDOException \$e) {
echo 'Connection failed: ' . \$e->getMessage(); try {
} \$dbh = Doctrine_Adapter::connect(\$dsn, \$user, \$password);
?>"); } catch (PDOException \$e) {
?> echo 'Connection failed: ' . \$e->getMessage();
<br \><br \> }
The next step is opening a new Doctrine_Connection. ?></code>
<br \><br \>
<?php
renderCode("<?php
\$conn = Doctrine_Manager::connection(\$dbh); The next step is opening a new Doctrine_Connection.
?>");
?>
<code type="php">
\$conn = Doctrine_Manager::connection(\$dbh);
?></code>
<?php ?> <?php ?>
Doctrine_Export drivers provide an easy database portable way of altering existing database tables. 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 NOTE: if you only want to get the generated sql (and not execute it) use Doctrine_Export::alterTableSql()
renderCode("<?php
\$dbh = new PDO('dsn','username','pw');
\$conn = Doctrine_Manager::getInstance()
->openConnection(\$dbh); <code type="php">
\$dbh = new PDO('dsn','username','pw');
\$a = array('add' => array('name' => array('type' => 'string', 'length' => 255))); \$conn = Doctrine_Manager::getInstance()
->openConnection(\$dbh);
\$conn->export->alterTableSql('mytable', \$a); \$a = array('add' => array('name' => array('type' => 'string', 'length' => 255)));
// On mysql this method returns:
// ALTER TABLE mytable ADD COLUMN name VARCHAR(255) \$conn->export->alterTableSql('mytable', \$a);
?>");
?> // On mysql this method returns:
<br \><br \> // ALTER TABLE mytable ADD COLUMN name VARCHAR(255)
Doctrine_Export::alterTable() takes two parameters: ?></code>
<br \><br \>
string <i>$name</i>
<dd>name of the table that is intended to be changed. <br \>
array <i>$changes</i> Doctrine_Export::alterTable() takes two parameters:
: 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.
The types of changes that are currently supported are defined as follows: : array //$changes// : associative array that contains the details of each type of change that is intended to be performed.
<ul> The types of changes that are currently supported are defined as follows:
<li \><i>name</i>
New name for the table. * //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.
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.
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.
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.
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.
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.
<code type="php">
$a = array('name' => 'userlist', <code type="php">
'add' => array( $a = array('name' => 'userlist',
'quota' => array( 'add' => array(
'type' => 'integer', 'quota' => array(
'unsigned' => 1 'type' => 'integer',
) 'unsigned' => 1
), )
'remove' => array( ),
'file_limit' => array(), 'remove' => array(
'time_limit' => array() 'file_limit' => array(),
), 'time_limit' => array()
'change' => array( ),
'name' => array( 'change' => array(
'length' => '20', 'name' => array(
'definition' => array( 'length' => '20',
'type' => 'text', 'definition' => array(
'length' => 20 'type' => 'text',
) 'length' => 20
) )
), )
'rename' => array( ),
'sex' => array( 'rename' => array(
'name' => 'gender', 'sex' => array(
'definition' => array( 'name' => 'gender',
'type' => 'text', 'definition' => array(
'length' => 1, 'type' => 'text',
'default' => 'M' 'length' => 1,
) 'default' => 'M'
) )
) )
)
);
);
$dbh = new PDO('dsn','username','pw');
$conn = Doctrine_Manager::getInstance()->openConnection($dbh); $dbh = new PDO('dsn','username','pw');
$conn = Doctrine_Manager::getInstance()->openConnection($dbh);
$conn->export->alterTable('mytable', $a);
</code> $conn->export->alterTable('mytable', $a);
</code>
<code type="php"> <code type="php">
$dbh = new PDO('dsn','username','pw'); $dbh = new PDO('dsn','username','pw');
$conn = Doctrine_Manager::getInstance()->openConnection($dbh); $conn = Doctrine_Manager::getInstance()->openConnection($dbh);
$fields = array('id' => array( $fields = array('id' => array(
'type' => 'integer', 'type' => 'integer',
'autoincrement' => true), 'autoincrement' => true),
'name' => array( 'name' => array(
'type' => 'string', 'type' => 'string',
'fixed' => true, 'fixed' => true,
'length' => 8) 'length' => 8)
); );
// the following option is mysql specific and // the following option is mysql specific and
// skipped by other drivers // skipped by other drivers
$options = array('type' => 'MYISAM'); $options = array('type' => 'MYISAM');
$conn->export->createTable('mytable', $fields); $conn->export->createTable('mytable', $fields);
// on mysql this executes query: // on mysql this executes query:
// CREATE TABLE mytable (id INT AUTO_INCREMENT PRIMARY KEY, // CREATE TABLE mytable (id INT AUTO_INCREMENT PRIMARY KEY,
// name CHAR(8)); // name CHAR(8));
</code> </code>
Syntax: Syntax:
<div class='sql'> <code>
<pre> operand comparison_operator ANY (subquery)
operand comparison_operator ANY (subquery) operand comparison_operator SOME (subquery)
operand comparison_operator SOME (subquery) operand comparison_operator ALL (subquery)
operand comparison_operator ALL (subquery) </code>
</pre>
</div> 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
An ALL conditional expression returns true if the comparison operation is true for all values is false if the result of the comparison is false for at least one row, and is unknown if neither true nor
in the result of the subquery or the result of the subquery is empty. An ALL conditional expression false.
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'> <code>
<pre> FROM C WHERE C.col1 < ALL (FROM C2(col1))
FROM C WHERE C.col1 < ALL (FROM C2(col1)) </code>
</pre>
</div> 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
An ANY conditional expression returns true if the comparison operation is true for some is empty or if the comparison operation is false for every value in the result of the subquery, and is
value in the result of the subquery. An ANY conditional expression is false if the result of the subquery unknown if neither true nor false.
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. <code>
FROM C WHERE C.col1 > ANY (FROM C2(col1))
<div class='sql'> </code>
<pre>
FROM C WHERE C.col1 > ANY (FROM C2(col1)) The keyword SOME is an alias for ANY.
</pre> <code>
</div> FROM C WHERE C.col1 > SOME (FROM C2(col1))
</code>
The keyword SOME is an alias for ANY.
<div class='sql'>
<pre> The comparison operators that can be used with ALL or ANY conditional expressions are =, <, <=, >, >=, <>. The
FROM C WHERE C.col1 > SOME (FROM C2(col1)) result of the subquery must be same type with the conditional expression.
</pre>
</div>
<br \>
The comparison operators that can be used with ALL or ANY conditional expressions are =, <, <=, >, >=, <>. The NOT IN is an alias for <> ALL. Thus, these two statements are equal:
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 \> <code>
<div class='sql'> FROM C WHERE C.col1 <> ALL (FROM C2(col1));
<pre> FROM C WHERE C.col1 NOT IN (FROM C2(col1));
FROM C WHERE C.col1 <> ALL (FROM C2(col1)); </code>
FROM C WHERE C.col1 NOT IN (FROM C2(col1));
</pre>
</div>
Syntax: Syntax:
<div class='sql'> <code>
<pre> //operand// [NOT ]EXISTS (//subquery//)
<i>operand</i> [NOT ]EXISTS (<i>subquery</i>) </code>
</pre> The EXISTS operator returns TRUE if the subquery returns one or more rows and FALSE otherwise.
</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 \> The NOT EXISTS operator returns TRUE if the subquery returns 0 rows and FALSE otherwise.
<br \>
Finding all articles which have readers:
<div class='sql'>
<pre> Finding all articles which have readers:
FROM Article <code>
WHERE EXISTS (FROM ReaderLog(id) FROM Article
WHERE ReaderLog.article_id = Article.id) WHERE EXISTS (FROM ReaderLog(id)
</pre> WHERE ReaderLog.article_id = Article.id)
</div> </code>
Finding all articles which don't have readers: Finding all articles which don't have readers:
<div class='sql'> <code>
<pre> FROM Article
FROM Article WHERE NOT EXISTS (FROM ReaderLog(id)
WHERE NOT EXISTS (FROM ReaderLog(id) WHERE ReaderLog.article_id = Article.id)
WHERE ReaderLog.article_id = Article.id) </code>
</pre>
</div>
Syntax: Syntax:
<div class='sql'> <code>
<pre> //operand// IN (//subquery//|//value list//)
<i>operand</i> IN (<i>subquery</i>|<i>value list</i>) </code>
</pre> An IN conditional expression returns true if the //operand// is found from result of the //subquery//
</div> or if its in the specificied comma separated //value list//, hence the IN expression is always false if the result of the subquery
An IN conditional expression returns true if the <i>operand</i> is found from result of the <i>subquery</i> is empty.
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
is empty. When //value list// is being used there must be at least one element in that list.
When <i>value list</i> is being used there must be at least one element in that list. <code>
FROM C1 WHERE C1.col1 IN (FROM C2(col1));
<div class='sql'>
<pre> FROM User WHERE User.id IN (1,3,4,5)
FROM C1 WHERE C1.col1 IN (FROM C2(col1)); </code>
FROM User WHERE User.id IN (1,3,4,5) The keyword IN is an alias for = ANY. Thus, these two statements are equal:
</pre> <code>
</div> FROM C1 WHERE C1.col1 = ANY (FROM C2(col1));
FROM C1 WHERE C1.col1 IN (FROM C2(col1));
The keyword IN is an alias for = ANY. Thus, these two statements are equal: </code>
<div class='sql'>
<pre>
FROM C1 WHERE C1.col1 = ANY (FROM C2(col1));
FROM C1 WHERE C1.col1 IN (FROM C2(col1));
</pre>
</div>
<code type="php"> <code type="php">
// POSITIONAL PARAMETERS: // POSITIONAL PARAMETERS:
$users = $conn->query("FROM User WHERE User.name = ?", array('Arnold')); $users = $conn->query("FROM User WHERE User.name = ?", array('Arnold'));
$users = $conn->query("FROM User WHERE User.id > ? AND User.name LIKE ?", array(50, 'A%')); $users = $conn->query("FROM User WHERE User.id > ? AND User.name LIKE ?", array(50, 'A%'));
// NAMED PARAMETERS: // NAMED PARAMETERS:
$users = $conn->query("FROM User WHERE User.name = :name", array(':name' => 'Arnold')); $users = $conn->query("FROM User WHERE User.name = :name", array(':name' => 'Arnold'));
$users = $conn->query("FROM User WHERE User.id > :id AND User.name LIKE :name", array(':id' => 50, ':name' => 'A%')); $users = $conn->query("FROM User WHERE User.id > :id AND User.name LIKE :name", array(':id' => 50, ':name' => 'A%'));
</code> </code>
Syntax:<br \> Syntax:
string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]
<br \> string_expression [NOT] LIKE pattern_value [ESCAPE escape_character]
<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 The string_expression must have a string value. The pattern_value is a string literal or a string-valued
input parameter (i.e., char or Character) and is used to escape the special meaning of the underscore input parameter in which an underscore (_) stands for any single character, a percent (%) character
and percent characters in pattern_value. stands for any sequence of characters (including the empty sequence), and all other characters stand for
<br \><br \> themselves. The optional escape_character is a single-character string literal or a character-valued
Examples: input parameter (i.e., char or Character) and is used to escape the special meaning of the underscore
<br \> and percent characters in pattern_value.
<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' Examples:
<li \>address.phone NOT LIKE ‘12%3’ is false for '123' and '12993' and true for '1234'
</ul>
<br \>
If the value of the string_expression or pattern_value is NULL or unknown, the value of the LIKE * address.phone LIKE 12%3 is true for '123' '12993' and false for '1234'
expression is unknown. If the escape_characteris specified and is NULL, the value of the LIKE expression * asentence.word LIKE l_se is true for lose and false for 'loose'
is unknown. * 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'
<code type="php">
// finding all users whose email ends with '@gmail.com'
$users = $conn->query("FROM User u, u.Email e WHERE e.address LIKE '%@gmail.com'"); 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
// finding all users whose name starts with letter 'A' is unknown.
$users = $conn->query("FROM User u WHERE u.name LIKE 'A%'");
</code> <code type="php">
// finding all users whose email ends with '@gmail.com'
$users = $conn->query("FROM User u, u.Email e WHERE e.address LIKE '%@gmail.com'");
// finding all users whose name starts with letter 'A'
$users = $conn->query("FROM User u WHERE u.name LIKE 'A%'");
</code>
<b>Strings</b><br \> **Strings**
A string literal is enclosed in single quotes—for example: 'literal'. A string literal that includes a single
quote is represented by two single quotes—for example: 'literal''s'. A string literal is enclosed in single quotesfor example: 'literal'. A string literal that includes a single
<div class='sql'> quote is represented by two single quotesfor example: 'literal''s'.
<pre> <code>
FROM User WHERE User.name = 'Vincent' FROM User WHERE User.name = 'Vincent'
</pre> </code>
</div>
**Integers**
<b>Integers</b><br \>
Integer literals support the use of PHP integer literal syntax. Integer literals support the use of PHP integer literal syntax.
<div class='sql'> <code>
<pre> FROM User WHERE User.id = 4
FROM User WHERE User.id = 4 </code>
</pre>
</div> **Floats**
<b>Floats</b><br \> Float literals support the use of PHP float literal syntax.
Float literals support the use of PHP float literal syntax. <code>
<div class='sql'> FROM Account WHERE Account.amount = 432.123
<pre> </code>
FROM Account WHERE Account.amount = 432.123
</pre>
</div>
**Booleans**
<br \>
<b>Booleans</b><br \> The boolean literals are true and false.
The boolean literals are true and false.
<code>
<div class='sql'> FROM User WHERE User.admin = true
<pre>
FROM User WHERE User.admin = true FROM Session WHERE Session.is_authed = false
</code>
FROM Session WHERE Session.is_authed = false
</pre>
</div>
**Enums**
<br \>
<b>Enums</b><br \> The enumerated values work in the same way as string literals.
The enumerated values work in the same way as string literals.
<code>
<div class='sql'> FROM User WHERE User.type = 'admin'
<pre> </code>
FROM User WHERE User.type = 'admin'
</pre>
</div>
Predefined reserved literals are case insensitive, although its a good standard to write them in uppercase.
<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. The operators are listed below in order of decreasing precedence.
<ul>
<li \> Navigation operator (.) * Navigation operator (.)
<li \> Arithmetic operators: <br \> * Arithmetic operators:
+, - unary <br \>
*, / multiplication and division <br \> +, - unary
+, - addition and subtraction <br \>
<li \> Comparison operators : =, >, >=, <, <=, <> (not equal), [NOT] LIKE, <br \> *, / multiplication and division
[NOT] IN, IS [NOT] NULL, IS [NOT] EMPTY <br \>
<li \> Logical operators:<br \> +, - addition and subtraction
NOT <br \>
AND <br \> * Comparison operators : =, >, >=, <, <=, <> (not equal), [NOT] LIKE,
OR <br \>
</ul> [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. 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. Some advantages of the subqueries:
<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.
<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.” * They provide alternative ways to perform operations that would otherwise require complex joins and unions.
</ul> * 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.”
<code type="php">
// finding all users which don't belong to any group 1
$query = "FROM User WHERE User.id NOT IN <code type="php">
(SELECT u.id FROM User u // finding all users which don't belong to any group 1
INNER JOIN u.Group g WHERE g.id = ?"; $query = "FROM User WHERE User.id NOT IN
(SELECT u.id FROM User u
$users = $conn->query($query, array(1)); INNER JOIN u.Group g WHERE g.id = ?";
// finding all users which don't belong to any groups $users = $conn->query($query, array(1));
// Notice:
// the usage of INNER JOIN // finding all users which don't belong to any groups
// the usage of empty brackets preceding the Group component // Notice:
// the usage of INNER JOIN
$query = "FROM User WHERE User.id NOT IN // the usage of empty brackets preceding the Group component
(SELECT u.id FROM User u
INNER JOIN u.Group g)"; $query = "FROM User WHERE User.id NOT IN
(SELECT u.id FROM User u
$users = $conn->query($query); INNER JOIN u.Group g)";
</code>
$users = $conn->query($query);
</code>
<div class='sql'> <code>
<pre> DELETE FROM //component_name//
DELETE FROM <i>component_name</i> [WHERE //where_condition//]
[WHERE <i>where_condition</i>] [ORDER BY ...]
[ORDER BY ...] [LIMIT //record_count//]
[LIMIT <i>record_count</i>] </code>
</pre>
</div> * The DELETE statement deletes records from //component_name// and returns the number of records deleted.
<ul>
<li \>The DELETE statement deletes records from <i>component_name</i> and returns the number of records deleted. * The optional WHERE clause specifies the conditions that identify which records to delete.
Without WHERE clause, all records are deleted.
<li \>The optional WHERE clause specifies the conditions that identify which records to delete.
Without WHERE clause, all records are deleted. * If the ORDER BY clause is specified, the records are deleted in the order that is specified.
<li \>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 > ?';
<code type="php">
$q = 'DELETE FROM Account WHERE id > ?'; $rows = $this->conn->query($q, array(3));
$rows = $this->conn->query($q, array(3)); // the same query using the query interface
// the same query using the query interface $q = new Doctrine_Query();
$q = new Doctrine_Query(); $rows = $q->delete('Account')
->from('Account a')
$rows = $q->delete('Account') ->where('a.id > ?', 3)
->from('Account a') ->execute();
->where('a.id > ?', 3)
->execute(); print $rows; // the number of affected rows
</code>
print $rows; // the number of affected rows
</code>
Syntax: <br \> Syntax:
<div class='sql'>
<pre> <code>
FROM <i>component_reference</i> [[LEFT | INNER] JOIN <i>component_reference</i>] ... FROM //component_reference// [[LEFT | INNER] JOIN //component_reference//] ...
</pre> </code>
</div>
The FROM clause indicates the component or components from which to retrieve records.
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.
If you name more than one component, you are performing a join. For each table specified, you can optionally specify an alias.
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'> * 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:
<pre> <code>
SELECT u.*, p.* FROM User u LEFT JOIN u.Phonenumber SELECT u.*, p.* FROM User u LEFT JOIN u.Phonenumber
SELECT u.*, p.* FROM User u, u.Phonenumber p SELECT u.*, p.* FROM User u, u.Phonenumber p
</pre> </code>
</div>
* //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).
<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 //INNER JOIN// can be used when you want to efficiently fetch for example all users which have one or more phonenumbers.
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. <code>
<div class='sql'> SELECT u.*, p.* FROM User u INNER JOIN u.Phonenumber p
<pre> </code>
SELECT u.*, p.* FROM User u INNER JOIN u.Phonenumber p
</pre>
</div>
<?php ?> <?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 map the concatenation of users firstname and lastname to a value called name
<br \><br \><?php
renderCode("<?php
\$q = new Doctrine_Query(); <code type="php">
\$q = new Doctrine_Query();
\$users = \$q->select('CONCAT(u.firstname, u.lastname) name')->from('User u')->execute();
\$users = \$q->select('CONCAT(u.firstname, u.lastname) name')->from('User u')->execute();
foreach(\$users as \$user) {
// here 'name' is not a property of \$user, foreach(\$users as \$user) {
// its a mapped function value // here 'name' is not a property of \$user,
print \$user->name; // 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 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. * The second and third arguments of the //SUBSTRING// function denote the starting position and length of
<br \><br \><?php the substring to be returned. These arguments are integers. The first position of a string is denoted by 1.
renderCode("<?php The //SUBSTRING// function returns a string.
\$q = new Doctrine_Query();
\$users = \$q->select('u.name')->from('User u')->where(\"SUBSTRING(u.name, 0, 1) = 'z'\")->execute(); <code type="php">
\$q = new Doctrine_Query();
foreach(\$users as \$user) {
print \$user->name; \$users = \$q->select('u.name')->from('User u')->where(\"SUBSTRING(u.name, 0, 1) = 'z'\")->execute();
}
?>"); 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
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 * The //TRIM// function trims the specified character from a string. If the character to be trimmed is not
renderCode("<?php specified, it is assumed to be space (or blank). The optional trim_character is a single-character string
\$q = new Doctrine_Query(); literal or a character-valued input parameter (i.e., char or Character)[30]. If a trim specification is
not provided, BOTH is assumed. The //TRIM// function returns the trimmed string.
\$users = \$q->select('u.name')->from('User u')->where(\"TRIM(u.name) = 'Someone'\")->execute();
foreach(\$users as \$user) { <code type="php">
print \$user->name; \$q = new Doctrine_Query();
}
?>"); \$users = \$q->select('u.name')->from('User u')->where(\"TRIM(u.name) = 'Someone'\")->execute();
?> <br \><br \>
<li \>The <i>LOWER</i> and <i>UPPER</i> functions convert a string to lower and upper case, respectively. They return a foreach(\$users as \$user) {
string. <br \><br \> print \$user->name;
}
<?php ?></code>
renderCode("<?php
\$q = new Doctrine_Query();
* The //LOWER// and //UPPER// functions convert a string to lower and upper case, respectively. They return a
\$users = \$q->select('u.name')->from('User u')->where(\"LOWER(u.name) = 'someone'\")->execute(); string.
foreach(\$users as \$user) {
print \$user->name;
} <code type="php">
?>"); \$q = new Doctrine_Query();
?> <br \><br \>
<li \> \$users = \$q->select('u.name')->from('User u')->where(\"LOWER(u.name) = 'someone'\")->execute();
The <i>LOCATE</i> 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 foreach(\$users as \$user) {
is the string to be located; the second argument is the string to be searched; the optional third argument print \$user->name;
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 ?></code>
returned.
<br \><br \> *
<li \>The <i>LENGTH</i> function returns the length of the string in characters as an integer. 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
</ul> 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.
* The //LENGTH// function returns the length of the string in characters as an integer.
<code type="php"> <code type="php">
$q = new Doctrine_Query(); $q = new Doctrine_Query();
$q->from('User')->where('User.Phonenumber.phonenumber.contains(?,?,?)'); $q->from('User')->where('User.Phonenumber.phonenumber.contains(?,?,?)');
$users = $q->execute(array('123 123 123', '0400 999 999', '+358 100 100')); $users = $q->execute(array('123 123 123', '0400 999 999', '+358 100 100'));
</code> </code>
<code type="php"> <code type="php">
$q = new Doctrine_Query(); $q = new Doctrine_Query();
$q->from('User')->where('User.Phonenumber.phonenumber.like(?,?)'); $q->from('User')->where('User.Phonenumber.phonenumber.like(?,?)');
$users = $q->execute(array('%123%', '456%')); $users = $q->execute(array('%123%', '456%'));
</code> </code>
<code type="php"> <code type="php">
$q = new Doctrine_Query(); $q = new Doctrine_Query();
$q->from('User')->where('User.Phonenumber.phonenumber.regexp(?,?)'); $q->from('User')->where('User.Phonenumber.phonenumber.regexp(?,?)');
$users = $q->execute(array('[123]', '^[3-5]')); $users = $q->execute(array('[123]', '^[3-5]'));
</code> </code>
<ul>
<li> GROUP BY and HAVING clauses can be used for dealing with aggregate functions <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 \> <li> Following aggregate functions are availible on DQL: COUNT, MAX, MIN, AVG, SUM
Selecting alphabetically first user by name.
<div class='sql'>
<pre> Selecting alphabetically first user by name.
SELECT MIN(u.name) FROM User u <code>
</pre> SELECT MIN(u.name) FROM User u
</div> </code>
Selecting the sum of all Account amounts. Selecting the sum of all Account amounts.
<div class='sql'> <code>
<pre> SELECT SUM(a.amount) FROM Account a
SELECT SUM(a.amount) FROM Account a </code>
</pre>
</div>
<br \> <li> Using an aggregate function in a statement containing no GROUP BY clause, results in grouping on all rows. In the example above
<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.
we fetch all users and the number of phonenumbers they have.
<code>
<div class='sql'> SELECT u.*, COUNT(p.id) FROM User u, u.Phonenumber p GROUP BY u.id
<pre> </code>
SELECT u.*, COUNT(p.id) FROM User u, u.Phonenumber p GROUP BY u.id
</pre>
</div>
<li> The HAVING clause can be used for narrowing the results using aggregate values. In the following example we fetch
<br \> all users which have atleast 2 phonenumbers
<li> The HAVING clause can be used for narrowing the results using aggregate values. In the following example we fetch <code>
all users which have atleast 2 phonenumbers SELECT u.* FROM User u, u.Phonenumber p HAVING COUNT(p.id) >= 2
<div class='sql'> </code>
<pre>
SELECT u.* FROM User u, u.Phonenumber p HAVING COUNT(p.id) >= 2
</pre>
</div>
<code type="php">
</ul>
// retrieve all users and the phonenumber count for each user
<code type="php"> $users = $conn->query("SELECT u.*, COUNT(p.id) count FROM User u, u.Phonenumber p GROUP BY u.id");
// retrieve all users and the phonenumber count for each user foreach($users as $user) {
print $user->name . ' has ' . $user->Phonenumber[0]->count . ' phonenumbers';
$users = $conn->query("SELECT u.*, COUNT(p.id) count FROM User u, u.Phonenumber p GROUP BY u.id"); }
</code>
foreach($users as $user) {
print $user->name . ' has ' . $user->Phonenumber[0]->count . ' phonenumbers';
}
</code>
Doctrine Query Language(DQL) is an Object Query Language created for helping users in complex object retrieval. 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). 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 When compared to using raw SQL, DQL has several benefits:
</ul>
<ul>
<li \>DQL understands relations so you don't have to type manually sql joins and join conditions * From the start it has been designed to retrieve records(objects) not result set rows
</ul>
<ul>
<li \>DQL is portable on different databases * DQL understands relations so you don't have to type manually sql joins and join conditions
</ul>
<ul>
<li \>DQL has some very complex built-in algorithms like (the record limit algorithm) which can help * DQL is portable on different databases
developer to efficiently retrieve objects
</ul>
<ul> * DQL has some very complex built-in algorithms like (the record limit algorithm) which can help
<li \>It supports some functions that can save time when dealing with one-to-many, many-to-many relational data with conditional fetching. developer to efficiently retrieve objects
</ul>
If the power of DQL isn't enough, you should consider using the rawSql API for object population. * It supports some functions that can save time when dealing with one-to-many, many-to-many relational data with conditional fetching.
<code type="php"> If the power of DQL isn't enough, you should consider using the rawSql API for object population.
// DO NOT USE THE FOLLOWING CODE
// (using many sql queries for object population):
<code type="php">
$users = $conn->getTable('User')->findAll(); // DO NOT USE THE FOLLOWING CODE
// (using many sql queries for object population):
foreach($users as $user) {
print $user->name."<br \>"; $users = $conn->getTable('User')->findAll();
foreach($user->Phonenumber as $phonenumber) {
print $phonenumber."<br \>"; foreach($users as $user) {
} print $user->name."
} ";
foreach($user->Phonenumber as $phonenumber) {
// same thing implemented much more efficiently: print $phonenumber."
// (using only one sql query for object population) ";
}
$users = $conn->query("FROM User.Phonenumber"); }
foreach($users as $user) { // same thing implemented much more efficiently:
print $user->name."<br \>"; // (using only one sql query for object population)
foreach($user->Phonenumber as $phonenumber) {
print $phonenumber."<br \>"; $users = $conn->query("FROM User.Phonenumber");
}
} foreach($users as $user) {
print $user->name."
</code> ";
foreach($user->Phonenumber as $phonenumber) {
print $phonenumber."
";
}
}
</code>
DQL LIMIT clause is portable on all supported databases. Special attention have been paid to following facts: 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 * Only Mysql, Pgsql and Sqlite implement LIMIT / OFFSET clauses natively
<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 * In Oracle / Mssql / Firebird LIMIT / OFFSET clauses need to be emulated in driver specific way
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
and it must avoid alias collisions with the main query.
* 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.
Propably the most complex feature DQL parser has to offer is its LIMIT clause parser. Not only Propably the most complex feature DQL parser has to offer is its LIMIT clause parser. Not only
does the DQL LIMIT clause parser take care of LIMIT database portability it is capable of limiting the number of records instead does the DQL LIMIT clause parser take care of LIMIT database portability it is capable of limiting the number of records instead
of rows by using complex query analysis and subqueries. of rows by using complex query analysis and subqueries.
The limit-subquery-algorithm is an algorithm that DQL parser uses internally when one-to-many / many-to-many relational 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 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. 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 \> 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
Now one might consider that adding a simple driver specific LIMIT 20 at the end of query would return the correct results. and all their related phonenumbers.
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 Now one might consider that adding a simple driver specific LIMIT 20 at the end of query would return the correct results.
for the tables in the subquery to avoid alias collisions. 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 QUERY:
<div class='sql'><pre> 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.
SELECT u.id, u.name, p.* FROM User u LEFT JOIN u.Phonenumber p LIMIT 20 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
</pre></div> for the tables in the subquery to avoid alias collisions.
SQL QUERY:
<div class='sql'>
<pre>
SELECT DQL QUERY:
e.id AS e__id, <div class='sql'><pre>
e.name AS e__name, SELECT u.id, u.name, p.* FROM User u LEFT JOIN u.Phonenumber p LIMIT 20
p.id AS p__id, </pre></div>
p.phonenumber AS p__phonenumber,
p.entity_id AS p__entity_id SQL QUERY:
FROM entity e <code>
LEFT JOIN phonenumber p ON e.id = p.entity_id SELECT
WHERE e.id IN ( e.id AS e__id,
SELECT DISTINCT e2.id e.name AS e__name,
FROM entity e2 p.id AS p__id,
WHERE (e2.type = 0) LIMIT 20) AND (e.type = 0) p.phonenumber AS p__phonenumber,
</pre> p.entity_id AS p__entity_id
</div> FROM entity e
LEFT JOIN phonenumber p ON e.id = p.entity_id
<br \><br \> WHERE e.id IN (
In the next example we are going to fetch first 20 users and all their phonenumbers and only SELECT DISTINCT e2.id
those users that actually have phonenumbers with single efficient query, hence we use an INNER JOIN. FROM entity e2
Notice how the DQL parser is smart enough to use the INNER JOIN in the subquery. WHERE (e2.type = 0) LIMIT 20) AND (e.type = 0)
</code>
<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> 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.
SQL QUERY: Notice how the DQL parser is smart enough to use the INNER JOIN in the subquery.
<div class='sql'>
<pre>
SELECT
e.id AS e__id,
e.name AS e__name, DQL QUERY:
p.id AS p__id, <div class='sql'><pre>
p.phonenumber AS p__phonenumber, SELECT u.id, u.name, p.* FROM User u LEFT JOIN u.Phonenumber p LIMIT 20
p.entity_id AS p__entity_id </pre></div>
FROM entity e
LEFT JOIN phonenumber p ON e.id = p.entity_id SQL QUERY:
WHERE e.id IN ( <code>
SELECT DISTINCT e2.id SELECT
FROM entity e2 e.id AS e__id,
INNER JOIN phonenumber p2 ON e2.id = p2.entity_id e.name AS e__name,
WHERE (e2.type = 0) LIMIT 20) AND (e.type = 0) p.id AS p__id,
</pre> p.phonenumber AS p__phonenumber,
</div> p.entity_id AS p__entity_id
FROM entity e
LEFT JOIN phonenumber p ON e.id = p.entity_id
WHERE e.id IN (
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)
</code>
<code type="php">
<code type="php">
// retrieve the first 20 users and all their associated phonenumbers
// retrieve the first 20 users and all their associated phonenumbers
$users = $conn->query("SELECT u.*, p.* FROM User u, u.Phonenumber p LIMIT 20");
$users = $conn->query("SELECT u.*, p.* FROM User u, u.Phonenumber p LIMIT 20");
foreach($users as $user) {
print ' --- '.$user->name.' --- \n'; foreach($users as $user) {
print ' --- '.$user->name.' --- \n';
foreach($user->Phonenumber as $p) {
print $p->phonenumber.'\n'; foreach($user->Phonenumber as $p) {
} print $p->phonenumber.'\n';
} }
</code> }
</code>
Record collections can be sorted efficiently at the database level using the ORDER BY clause. Record collections can be sorted efficiently at the database level using the ORDER BY clause.
Syntax: Syntax:
<div class='sql'> <code>
<pre> [ORDER BY {ComponentAlias.columnName}
[ORDER BY {ComponentAlias.columnName} [ASC | DESC], ...]
[ASC | DESC], ...] </code>
</pre>
</div> Examples:
Examples:
<br \> <code>
<div class='sql'> FROM User.Phonenumber
<pre> ORDER BY User.name, Phonenumber.phonenumber
FROM User.Phonenumber
ORDER BY User.name, Phonenumber.phonenumber FROM User u, u.Email e
ORDER BY e.address, u.id
FROM User u, u.Email e </code>
ORDER BY e.address, u.id 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.
</pre>
</div>
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. <code>
<br \> FROM User u, u.Email e
<div class='sql'> ORDER BY e.address DESC, u.id ASC;
<pre> </code>
FROM User u, u.Email e
ORDER BY e.address DESC, u.id ASC;
</pre>
</div>
Aggregate value SELECT syntax: Aggregate value SELECT syntax:
<code type="php"> <code type="php">
// SELECT u.*, COUNT(p.id) num_posts FROM User u, u.Posts p WHERE u.id = 1 GROUP BY u.id // SELECT u.*, COUNT(p.id) num_posts FROM User u, u.Posts p WHERE u.id = 1 GROUP BY u.id
$query = new Doctrine_Query(); $query = new Doctrine_Query();
$query->select('u.*, COUNT(p.id) num_posts') $query->select('u.*, COUNT(p.id) num_posts')
->from('User u, u.Posts p') ->from('User u, u.Posts p')
->where('u.id = ?', 1) ->where('u.id = ?', 1)
->groupby('u.id'); ->groupby('u.id');
$users = $query->execute(); $users = $query->execute();
echo $users->Posts[0]->num_posts . ' posts found'; echo $users->Posts[0]->num_posts . ' posts found';
</code> </code>
SELECT statement syntax: SELECT statement syntax:
<div class='sql'> <code>
<pre> SELECT
SELECT [ALL | DISTINCT]
[ALL | DISTINCT] //select_expr//, ...
<i>select_expr</i>, ... [FROM //components//
[FROM <i>components</i> [WHERE //where_condition//]
[WHERE <i>where_condition</i>] [GROUP BY //groupby_expr//
[GROUP BY <i>groupby_expr</i> [ASC | DESC], ... ]
[ASC | DESC], ... ] [HAVING //where_condition//]
[HAVING <i>where_condition</i>] [ORDER BY //orderby_expr//
[ORDER BY <i>orderby_expr</i> [ASC | DESC], ...]
[ASC | DESC], ...] [LIMIT //row_count// OFFSET //offset//}]
[LIMIT <i>row_count</i> OFFSET <i>offset</i>}] </code>
</pre>
</div>
<br \> The SELECT statement is used for the retrieval of data from one or more components.
The SELECT statement is used for the retrieval of data from one or more components.
<ul> * 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.
<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. <code>
<div class='sql'> SELECT a.name, a.amount FROM Account a
<pre> </code>
SELECT a.name, a.amount FROM Account a
</pre> * 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
</div> (Doctrine converts asterisk to appropriate column names, hence leading to better performance on some databases).
<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 SELECT a.* FROM Account a
(Doctrine converts asterisk to appropriate column names, hence leading to better performance on some databases). </code>
<div class='sql'> * FROM clause //components// indicates the component or components from which to retrieve records.
<pre> <code>
SELECT a.* FROM Account a SELECT a.* FROM Account a
</pre>
</div> SELECT u.*, p.*, g.* FROM User u LEFT JOIN u.Phonenumber p LEFT JOIN u.Group g
<li \>FROM clause <i>components</i> indicates the component or components from which to retrieve records. </code>
<div class='sql'> * 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.
<pre> <code>
SELECT a.* FROM Account a SELECT a.* FROM Account a WHERE a.amount > 2000
</code>
SELECT u.*, p.*, g.* FROM User u LEFT JOIN u.Phonenumber p LEFT JOIN u.Group g * In the WHERE clause, you can use any of the functions and operators that DQL supports, except for aggregate (summary) functions
</pre>
</div> * The HAVING clause can be used for narrowing the results with aggregate functions
<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. <code>
<div class='sql'> SELECT u.* FROM User u LEFT JOIN u.Phonenumber p HAVING COUNT(p.id) > 3
<pre> </code>
SELECT a.* FROM Account a WHERE a.amount > 2000 * The ORDER BY clause can be used for sorting the results
</pre> <code>
</div> SELECT u.* FROM User u ORDER BY u.name
<li \>In the WHERE clause, you can use any of the functions and operators that DQL supports, except for aggregate (summary) functions </code>
* The LIMIT and OFFSET clauses can be used for efficiently limiting the number of records to a given //row_count//
<li \>The HAVING clause can be used for narrowing the results with aggregate functions <code>
<div class='sql'> SELECT u.* FROM User u LIMIT 20
<pre> </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>
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>
SELECT u.* FROM User u LIMIT 20
</pre>
</div>
</ul>
UPDATE statement syntax: UPDATE statement syntax:
<div class='sql'> <code>
<pre> UPDATE //component_name//
UPDATE <i>component_name</i> SET //col_name1//=//expr1// [, //col_name2//=//expr2// ...]
SET <i>col_name1</i>=<i>expr1</i> [, <i>col_name2</i>=<i>expr2</i> ...] [WHERE //where_condition//]
[WHERE <i>where_condition</i>] [ORDER BY ...]
[ORDER BY ...] [LIMIT //record_count//]
[LIMIT <i>record_count</i>] </code>
</pre>
</div> * The UPDATE statement updates columns of existing records in //component_name// with new values and returns the number of affected records.
<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. * The SET clause indicates which columns to modify and the values they should be given.
<li \>The SET clause indicates which columns to modify and the values they should be given. * The optional WHERE clause specifies the conditions that identify which records to update.
Without WHERE clause, all records are updated.
<li \>The optional WHERE clause specifies the conditions that identify which records to update.
Without WHERE clause, all records are updated. * The optional ORDER BY clause specifies the order in which the records are being updated.
<li \>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.
<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. The statement stops as soon as it has found //record_count// rows that satisfy the WHERE clause, whether or not they actually were changed.
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';
<code type="php">
$q = 'UPDATE Account SET amount = amount + 200 WHERE id > 200'; $rows = $this->conn->query($q);
$rows = $this->conn->query($q); // the same query using the query interface
// the same query using the query interface $q = new Doctrine_Query();
$q = new Doctrine_Query(); $rows = $q->update('Account')
->set('amount', 'amount + 200')
$rows = $q->update('Account') ->where('id > 200')
->set('amount', 'amount + 200') ->execute();
->where('id > 200')
->execute(); print $rows; // the number of affected rows
</code>
print $rows; // the number of affected rows
</code>
<code type="php"> <code type="php">
$sess = Doctrine_Manager::getInstance()->openConnection(new PDO("dsn","username","password")); $sess = Doctrine_Manager::getInstance()->openConnection(new PDO("dsn","username","password"));
// select first ten rows starting from the row 20 // select first ten rows starting from the row 20
$sess->select("select * from user",10,20); $sess->select("select * from user",10,20);
</code> </code>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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