1
0
mirror of synced 2024-12-14 23:26:04 +03:00

Misc. fixes and initial entry of PHP Mapping and Metadata Drivers chapters.

This commit is contained in:
Jonathan H. Wage 2010-04-26 11:55:43 -04:00
parent fc290404e1
commit 605ee881c5
8 changed files with 412 additions and 40 deletions

View File

@ -14,8 +14,10 @@
+ XML Mapping
+ YAML Mapping
+ Annotations Reference
+ PHP Mapping
+ Caching
+ Improving Performance
+ Tools
+ Metadata Drivers
+ DBAL
+ Best Practices

View File

@ -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
...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

View File

@ -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]
<?php
// ...
class MyEntity implements NotifyPropertyChanged
@ -396,7 +396,7 @@ Proxy classes can either be generated manually through the Doctrine Console or a
The default value is `TRUE` for convenient development. However, this setting is not optimal for performance and therefore not recommended for a production environment.
To eliminate the overhead of proxy class generation during runtime, set this configuration option to `FALSE`. When you do this in a development environment, note that you may get class/file not found errors if certain proxy classes are not available or failing lazy-loads if new methods were added to the entity class that are not yet in the proxy class. In such a case, simply use the Doctrine Console to (re)generate the proxy classes like so:
doctrine orm:generate-proxies
$ ./doctrine orm:generate-proxies
++ Multiple Metadata Sources

View File

@ -7,7 +7,7 @@ state and mapping information that is common to multiple entity classes.
Mapped superclasses, just as regular, non-mapped classes, can appear in the middle of an otherwise
mapped inheritance hierarchy (through Single Table Inheritance or Class Table Inheritance).
> *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

View File

@ -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,11 +130,18 @@ 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.doctrine-project.org/trunk doctrine
$ svn co http://svn.github.com/doctrine/doctrine2.git doctrine2
++ Sandbox Quickstart
@ -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

View File

@ -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";
}

199
manual/en/php-mapping.txt Normal file
View File

@ -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)`

View File

@ -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;
/**