diff --git a/manual/en.txt b/manual/en.txt index 7cc3be93f..56e3ff339 100644 --- a/manual/en.txt +++ b/manual/en.txt @@ -14,8 +14,10 @@ + XML Mapping + YAML Mapping + Annotations Reference ++ PHP Mapping + Caching + Improving Performance + Tools ++ Metadata Drivers + DBAL + Best Practices \ No newline at end of file diff --git a/manual/en/association-mapping.txt b/manual/en/association-mapping.txt index 5190f6a14..02c59675d 100644 --- a/manual/en/association-mapping.txt +++ b/manual/en/association-mapping.txt @@ -412,18 +412,27 @@ to all the explicitly given ORDER BY items. * To keep the database impact low, these implicit ORDER BY items are only added to an DQL Query if the collection is fetch joined in the DQL query. -Given our previously defined Example: +Given our previously defined example, the following would not add ORDER BY, since g is not fetch joined: [sql] - -- Would not add ORDER BY, since g is not fetch joined SELECT u FROM User u JOIN u.groups g WHERE SIZE(g) > 10 - -- However +However the following: + + [sql] SELECT u FROM User u JOIN u.groups g WHERE u.id = 10 - -- would internally be rewritten to + +...would internally be rewritten to: + + [sql] SELECT u FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name ASC - -- You can't reverse the order, an explicit: +You can't reverse the order, an explicit: + + [sql] SELECT u FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC - -- is internally be rewritten to - SELECT u FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC, g.name ASC + +...is internally rewritten to: + + [sql] + SELECT u FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC, g.name ASC \ No newline at end of file diff --git a/manual/en/configuration.txt b/manual/en/configuration.txt index f3f8a4fbe..6ddb421aa 100644 --- a/manual/en/configuration.txt +++ b/manual/en/configuration.txt @@ -158,9 +158,9 @@ The recommended implementations are: +++ SQL Logger (***Optional***) - [php] - $config->setSQLLogger($logger); - $config->getSQLLogger(); + [php] + $config->setSQLLogger($logger); + $config->getSQLLogger(); Gets or sets the logger to use for logging all SQL statements executed by Doctrine. The logger class must implement the `Doctrine\DBAL\Logging\SqlLogger` interface. A simple default implementation that logs to the standard output using `echo` and `var_dump` can be found at `Doctrine\DBAL\Logging\EchoSqlLogger`. @@ -191,14 +191,14 @@ be defined on a per-class basis (or more precisely, per-hierarchy). +++ Deferred Implicit The deferred implicit policy is the default change tracking policy and the most - convenient one. With this policy, Doctrine detects the changes by a - property-by-property comparison at commit time and also detects changes - to entities or new entities that are referenced by other managed entities - ("persistence by reachability"). Although the most convenient policy, it can - have negative effects on performance if you are dealing with large units of - work (see "Understanding the Unit of Work"). Since Doctrine can't know what - has changed, it needs to check all managed entities for changes every time you - invoke EntityManager#flush(), making this operation rather costly. +convenient one. With this policy, Doctrine detects the changes by a +property-by-property comparison at commit time and also detects changes +to entities or new entities that are referenced by other managed entities +("persistence by reachability"). Although the most convenient policy, it can +have negative effects on performance if you are dealing with large units of +work (see "Understanding the Unit of Work"). Since Doctrine can't know what +has changed, it needs to check all managed entities for changes every time you +invoke EntityManager#flush(), making this operation rather costly. +++ Deferred Explicit @@ -218,6 +218,7 @@ on the way so you can pass them to EntityManager#persist(). This policy can be configured as follows: + [php] /** * @Entity * @ChangeTrackingPolicy("DEFERRED_EXPLICIT") @@ -268,7 +269,6 @@ Then, in each property setter of this class or derived classes, you need to invoke `_onPropertyChanged` as follows to notify listeners: [php] - *NOTICE* +> **NOTE** > > A mapped superclass cannot be an entity, it is not queryable and persistent relationships defined by a mapped > superclass must be unidirectional. For further support of inheritance, the single or joined table inheritance diff --git a/manual/en/introduction.txt b/manual/en/introduction.txt index 1b4d7bf6c..76206e811 100644 --- a/manual/en/introduction.txt +++ b/manual/en/introduction.txt @@ -10,7 +10,9 @@ unnecessary code duplication. ++ Disclaimer -This is the Doctrine 2 reference documentation. Introductory guides and tutorials that you can follow along from start to finish, like the "Guide to Doctrine" book known from the Doctrine 1.x series, will be available at a later date. +This is the Doctrine 2 reference documentation. Introductory guides and tutorials +that you can follow along from start to finish, like the "Guide to Doctrine" book +known from the Doctrine 1.x series, will be available at a later date. ++ Requirements @@ -64,19 +66,19 @@ line installation utility. To install just the `Common` package you can run the following command: - $ sudo pear install pear.phpdoctrine.org/DoctrineCommon-2.0.0 + $ sudo pear install pear.doctrine-project.org/DoctrineCommon-2.0.0 If you want to use the Doctrine Database Abstraction Layer you can install it with the following command. - $ sudo pear install pear.phpdoctrine.org/DoctrineDBAL-2.0.0 + $ sudo pear install pear.doctrine-project.org/DoctrineDBAL-2.0.0 Or, if you want to get the works and go for the ORM you can install it with the following command. - $ sudo pear install pear.phpdoctrine.org/DoctrineORM-2.0.0 + $ sudo pear install pear.doctrine-project.org/DoctrineORM-2.0.0 -When you have a package installed via PEAR you can required and load the +When you have a package installed via PEAR you can require and load the `ClassLoader` with the following code. [php] @@ -128,12 +130,19 @@ see what you can do with it. You can also use Doctrine 2 by downloading the latest release package from [the download page](http://www.doctrine-project.org/download). ++++ GitHub + +Alternatively you can clone the latest version of Doctrine 2 via GitHub.com: + + $ git clone git://github.com/doctrine/doctrine2.git doctrine + +++ Subversion -Alternatively you can check out the latest version of Doctrine 2 via SVN. +If you prefer subversion you can also checkout the code from GitHub.com through +the subversion protocol: + + $ svn co http://svn.github.com/doctrine/doctrine2.git doctrine2 - $ svn co http://svn.doctrine-project.org/trunk doctrine - ++ Sandbox Quickstart > **NOTE** @@ -183,7 +192,7 @@ see the same output. 2) Take another look into the tools/sandbox folder. A SQLite database should have been created with the name `database.sqlite`. -3) Open `index.php` and edit it so that it looks as follows: +3) Open `index.php` and at the bottom edit it so it looks like the following: [php] //... bootstrap stuff @@ -241,4 +250,4 @@ but we wanted to introduce you to DQL at this point. Can you **find** the easier > schema with the command `doctrine orm:schema-tool --drop` followed by > `doctrine orm:schema-tool --create`. -7) Explore Doctrine 2! +7) Explore Doctrine 2! \ No newline at end of file diff --git a/manual/en/metadata-drivers.txt b/manual/en/metadata-drivers.txt new file mode 100644 index 000000000..b07310346 --- /dev/null +++ b/manual/en/metadata-drivers.txt @@ -0,0 +1,155 @@ +The heart of an object relational mapper is the mapping information that glues +everything together. It instructs the EntityManager how it should behave when +dealing with the different entities. + +++ Core Metadata Drivers + +Doctrine provides a few different ways for you to specify your metadata: + + * **XML files** (XmlDriver) + * **Class DocBlock Annotations** (AnnotationDriver) + * **YAML files** (YamlDriver) + * **PHP Code in files or static functions** (PhpDriver) + +Something important to note about the above drivers is they are all an intermediate +step to the same end result. The mapping information is populated to +`Doctrine\ORM\Mapping\ClassMetadata` instances. So in the end, Doctrine +only ever has to work with the api of the `ClassMetadata` class to get mapping +information for an entity. + +> **TIP** +> The populated `ClassMetadata` instances are also cached so in a production +> environment the parsing and populating only ever happens once. You can configure +> the metadata cache implementation using the `setMetadataCacheImpl()` method on +> the `Doctrine\ORM\Configuration` class: +> +> [php] +> $em->getConfiguration()->setMetadataCacheImpl(new ApcCache()); + +If you want to use one of the included core metadata drivers you just need to +configure it. All the drivers are in the `Doctrine\ORM\Mapping\Driver` namespace: + + [php] + $driver = new \Doctrine\ORM\Mapping\Driver\XmlDriver('/path/to/mapping/files'); + $em->getConfiguration()->setMetadataDriverImpl($driver); + +++ Implementing Metadata Drivers + +In addition to the included metadata drivers you can very easily implement +your own. All you need to do is define a class which implements the `Driver` +interface: + + [php] + namespace Doctrine\ORM\Mapping\Driver; + + use Doctrine\ORM\Mapping\ClassMetadataInfo; + + interface Driver + { + /** + * Loads the metadata for the specified class into the provided container. + * + * @param string $className + * @param ClassMetadataInfo $metadata + */ + function loadMetadataForClass($className, ClassMetadataInfo $metadata); + + /** + * Gets the names of all mapped classes known to this driver. + * + * @return array The names of all mapped classes known to this driver. + */ + function getAllClassNames(); + + /** + * Whether the class with the specified name should have its metadata loaded. + * This is only the case if it is either mapped as an Entity or a + * MappedSuperclass. + * + * @param string $className + * @return boolean + */ + function isTransient($className); + } + +If you want to write a metadata driver to parse information from some file format +we've made your life a little easier by providing the `AbstractFileDriver` +implementation for you to extend from: + + [php] + class MyMetadataDriver extends AbstractFileDriver + { + /** + * {@inheritdoc} + */ + protected $_fileExtension = '.dcm.ext'; + + /** + * {@inheritdoc} + */ + public function loadMetadataForClass($className, ClassMetadataInfo $metadata) + { + $data = $this->_loadMappingFile($file); + + // populate ClassMetadataInfo instance from $data + } + + /** + * {@inheritdoc} + */ + protected function _loadMappingFile($file) + { + // parse contents of $file and return php data structure + } + } + +> **NOTE** +> When using the `AbstractFileDriver` it requires that you only have one entity +> defined per file and the file named after the class described inside where +> namespace separators are replaced by periods. So if you have an entity named +> `Entities\User` and you wanted to write a mapping file for your driver above +> you would need to name the file `Entities.User.dcm.ext` for it to be recognized. + +Now you can use your `MyMetadataDriver` implementation by setting it with the +`setMetadataDriverImpl()` method: + + [php] + $driver = new MyMetadataDriver('/path/to/mapping/files'); + $em->getConfiguration()->setMetadataDriverImpl($driver); + +++ ClassMetadata + +The last piece you need to know and understand about metadata in Doctrine 2 is +the API of the `ClassMetadata` classes. You need to be familiar with them in order +to implement your own drivers but more importantly to retrieve mapping information +for a certain entity when needed. + +You have all the methods you need to manually specify the mapping information +instead of using some mapping file to populate it from. The base `ClassMetadataInfo` +class is responsible for only data storage and is not meant for runtime use. It +does not require that the class actually exists yet so it is useful for describing some +entity before it exists and using that information to generate for example the +entities themselves. The class `ClassMetadata` extends `ClassMetadataInfo` and +adds some functionality required for runtime usage and requires that the PHP +class is present and can be autoloaded. + +You can read more about the API of the `ClassMetadata` classes in the PHP Mapping +chapter. + +++ Getting ClassMetadata Instances + +If you want to get the `ClassMetadata` instance for an entity in your project +to programatically use some mapping information to generate some HTML or something +similar you can retrieve it through the `ClassMetadataFactory`: + + [php] + $cmf = $em->getMetadataFactory(); + $class = $cmf->getMetadataFor('MyEntityName'); + +Now you can learn about the entity and use the data stored in the `ClassMetadata` +instance to get all mapped fields for example and iterate over them: + + [php] + foreach ($class->fieldMappings as $fieldMapping) { + echo $fieldMapping['fieldName'] . "\n"; + } \ No newline at end of file diff --git a/manual/en/php-mapping.txt b/manual/en/php-mapping.txt new file mode 100644 index 000000000..19a4f66a5 --- /dev/null +++ b/manual/en/php-mapping.txt @@ -0,0 +1,199 @@ +Doctrine 2 also allows you to provide the ORM metadata in the form of plain +PHP code using the `ClassMetadata` API. You can write the code in PHP files or +inside of a static function named `loadMetadata($class)` on the entity class itself. + +++ PHP Files + +If you wish to write your mapping information inside PHP files that are named +after the entity and included to populate the metadata for an entity you can do +so by using the `PHPDriver`: + + [php] + $driver = new PHPDriver('/path/to/php/mapping/files'); + $em->getConfiguration()->setMetadataDriverImpl($driver); + +Now imagine we had an entity named `Entities\User` and we wanted to write a mapping +file for it using the above configured `PHPDriver` instance: + + [php] + namespace Entities; + + class User + { + private $id; + private $username; + } + +To write the mapping information you just need to create a file named +`Entities.User.php` inside of the `/path/to/php/mapping/files` folder: + + [php] + // /path/to/php/mapping/files/Entities.User.php + + $metadata->mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'username', + 'type' => 'string' + )); + +Now we can easily retrieve the populated `ClassMetadata` instance where the `PHPDriver` +includes the file and the `ClassMetadataFactory` caches it for later retrieval: + + [php] + $class = $em->getMetadataFor('Entities\User'); + +++ Static Function + +In addition to the PHP files you can also specify your mapping information inside +of a static function defined on the entity class itself. This is useful for cases +where you want to keep your entity and mapping information together but don't want +to use annotations. For this you just need to use the `StaticPHPDriver`: + + [php] + $driver = new StaticPHPDriver('/path/to/entities'); + $em->getConfiguration()->setMetadataDriverImpl($driver); + +Now you just need to define a static function named `loadMetadata($metadata)` on your entity: + + [php] + namespace Entities; + + use Doctrine\ORM\Mapping\ClassMetadata; + + class User + { + // ... + + public static function loadMetadata(ClassMetadata $metadata) + { + $metadata->mapField(array( + 'id' => true, + 'fieldName' => 'id', + 'type' => 'integer' + )); + + $metadata->mapField(array( + 'fieldName' => 'username', + 'type' => 'string' + )); + } + } + +++ ClassMetadataInfo API + +The `ClassMetadataInfo` class is the base data object for storing the mapping +metadata for a single entity. It contains all the getters and setters you need +populate and retrieve information for an entity. + ++++ General Setters + + * `setTableName($tableName)` + * `setPrimaryTable(array $primaryTableDefinition)` + * `setCustomRepositoryClass($repositoryClassName)` + * `setIdGeneratorType($generatorType)` + * `setIdGenerator($generator)` + * `setSequenceGeneratorDefinition(array $definition)` + * `setChangeTrackingPolicy($policy)` + * `setIdentifier(array $identifier)` + ++++ Inheritance Setters + + * `setInheritanceType($type)` + * `setSubclasses(array $subclasses)` + * `setParentClasses(array $classNames)` + * `setDiscriminatorColumn($columnDef)` + * `setDiscriminatorMap(array $map)` + ++++ Field Mapping Setters + + * `mapField(array $mapping)` + * `mapOneToOne(array $mapping)` + * `mapOneToMany(array $mapping)` + * `mapManyToOne(array $mapping)` + * `mapManyToMany(array $mapping)` + ++++ Lifecycle Callback Setters + + * `addLifecycleCallback($callback, $event)` + * `setLifecycleCallbacks(array $callbacks)` + ++++ Versioning Setters + + * `setVersionMapping(array &$mapping)` + * `setVersioned($bool)` + * `setVersionField()` + ++++ General Getters + + * `getTableName()` + * `getTemporaryIdTableName()` + ++++ Identifier Getters + + * `getIdentifierColumnNames()` + * `usesIdGenerator()` + * `isIdentifier($fieldName)` + * `isIdGeneratorIdentity()` + * `isIdGeneratorSequence()` + * `isIdGeneratorTable()` + * `isIdentifierNatural()` + * `getIdentifierFieldNames()` + * `getSingleIdentifierFieldName()` + * `getSingleIdentifierColumnName()` + ++++ Inheritance Getters + + * `isInheritanceTypeNone()` + * `isInheritanceTypeJoined()` + * `isInheritanceTypeSingleTable()` + * `isInheritanceTypeTablePerClass()` + * `isInheritedField($fieldName)` + * `isInheritedAssociation($fieldName)` + ++++ Change Tracking Getters + + * `isChangeTrackingDeferredExplicit()` + * `isChangeTrackingDeferredImplicit()` + * `isChangeTrackingNotify()` + ++++ Field & Association Getters + + * `isUniqueField($fieldName)` + * `isNullable($fieldName)` + * `getColumnName($fieldName)` + * `getFieldMapping($fieldName)` + * `getAssociationMapping($fieldName)` + * `getAssociationMappings()` + * `getFieldName($columnName)` + * `hasField($fieldName)` + * `getColumnNames(array $fieldNames = null)` + * `getTypeOfField($fieldName)` + * `getTypeOfColumn($columnName)` + * `hasAssociation($fieldName)` + * `isSingleValuedAssociation($fieldName)` + * `isCollectionValuedAssociation($fieldName)` + ++++ Lifecycle Callback Getters + + * `hasLifecycleCallbacks($lifecycleEvent)` + * `getLifecycleCallbacks($event)` + +++ ClassMetadata API + +The `ClassMetadata` class extends `ClassMetadataInfo` and adds the runtime functionality +required by Doctrine. It adds a few extra methods related to runtime reflection +for working with the entities themselves. + + * `getReflectionClass()` + * `getReflectionProperties()` + * `getReflectionProperty($name)` + * `getSingleIdReflectionProperty()` + * `getIdentifierValues($entity)` + * `setIdentifierValues($entity, $id)` + * `setFieldValue($entity, $field, $value)` + * `getFieldValue($entity, $field)` \ No newline at end of file diff --git a/manual/en/working-with-objects.txt b/manual/en/working-with-objects.txt index d6c76ad07..43fdce820 100644 --- a/manual/en/working-with-objects.txt +++ b/manual/en/working-with-objects.txt @@ -68,13 +68,6 @@ synchronized with the database when `EntityManager#flush()` is invoked. > with the database in the most efficient way and a single, short transaction, > taking care of maintaining referential integrity. -- - -> **CAUTION** -> Generated entity identifiers / primary keys are guaranteed to be available after the -> next invocation of `EntityManager#flush()` that involves the entity in question. -> YOU CAN NOT RELY ON A GENERATED IDENTIFIER TO BE AVAILABLE AFTER INVOKING `persist`! - Example: [php] @@ -82,7 +75,11 @@ Example: $user->setName('Mr.Right'); $em->persist($user); $em->flush(); - // If $user had a generated identifier, it would now be available. + +> **CAUTION** +> Generated entity identifiers / primary keys are guaranteed to be available after the +> next invocation of `EntityManager#flush()` that involves the entity in question. +> YOU CAN NOT RELY ON A GENERATED IDENTIFIER TO BE AVAILABLE AFTER INVOKING `persist`! The semantics of the persist operation, applied on an entity X, are as follows: @@ -161,7 +158,7 @@ Example: // $entity now refers to the fully managed copy returned by the merge operation. // The EntityManager $em now manages the persistence of $entity as usual. -> **WARNING** +> **CAUTION** > When you want to serialize/unserialize entities you have to make all entity properties > protected, never private. The reason for this is, if you serialize a class that was a proxy > instance before the private variables won't be serialized and a PHP Notice is thrown. @@ -417,6 +414,7 @@ one recommended way of grouping these queries in a central location. [php] namespace MyDomain\Model; + use Doctrine\ORM\EntityRepository; /**