Add Limitations and Known Issues Appendix to the manual
This commit is contained in:
parent
93c061b7a5
commit
a6b5ee3cd3
@ -22,4 +22,5 @@
|
||||
+ Improving Performance
|
||||
+ Tools
|
||||
+ Metadata Drivers
|
||||
+ Best Practices
|
||||
+ Best Practices
|
||||
+ Limitations and Known Issues
|
128
manual/en/change-tracking-policies.txt
Normal file
128
manual/en/change-tracking-policies.txt
Normal file
@ -0,0 +1,128 @@
|
||||
++ Change Tracking Policies
|
||||
|
||||
Change tracking is the process of determining what has changed in managed
|
||||
entities since the last time they were synchronized with the database.
|
||||
|
||||
Doctrine provides 3 different change tracking policies, each having its
|
||||
particular advantages and disadvantages. The change tracking policy can
|
||||
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.
|
||||
|
||||
+++ Deferred Explicit
|
||||
|
||||
The deferred explicit policy is similar to the deferred implicit policy in that
|
||||
it detects changes through a property-by-property comparison at commit time. The
|
||||
difference is that only entities are considered that have been explicitly marked
|
||||
for change detection through a call to EntityManager#persist(entity) or through
|
||||
a save cascade. All other entities are skipped. This policy therefore gives
|
||||
improved performance for larger units of work while sacrificing the behavior
|
||||
of "automatic dirty checking".
|
||||
|
||||
Therefore, flush() operations are potentially cheaper with this policy. The
|
||||
negative aspect this has is that if you have a rather large application and
|
||||
you pass your objects through several layers for processing purposes and
|
||||
business tasks you may need to track yourself which entities have changed
|
||||
on the way so you can pass them to EntityManager#persist().
|
||||
|
||||
This policy can be configured as follows:
|
||||
|
||||
[php]
|
||||
/**
|
||||
* @Entity
|
||||
* @ChangeTrackingPolicy("DEFERRED_EXPLICIT")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
+++ Notify
|
||||
|
||||
This policy is based on the assumption that the entities notify interested
|
||||
listeners of changes to their properties. For that purpose, a class that
|
||||
wants to use this policy needs to implement the `NotifyPropertyChanged`
|
||||
interface from the Doctrine\Common namespace. As a guideline, such an
|
||||
implementation can look as follows:
|
||||
|
||||
[php]
|
||||
use Doctrine\Common\NotifyPropertyChanged,
|
||||
Doctrine\Common\PropertyChangedListener;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @ChangeTrackingPolicy("NOTIFY")
|
||||
*/
|
||||
class MyEntity implements NotifyPropertyChanged
|
||||
{
|
||||
// ...
|
||||
|
||||
private $_listeners = array();
|
||||
|
||||
public function addPropertyChangedListener(PropertyChangedListener $listener)
|
||||
{
|
||||
$this->_listeners[] = $listener;
|
||||
}
|
||||
}
|
||||
|
||||
Then, in each property setter of this class or derived classes, you need to
|
||||
notify all the `PropertyChangedListener` instances. As an example we
|
||||
add a convenience method on `MyEntity` that shows this behaviour:
|
||||
|
||||
[php]
|
||||
// ...
|
||||
|
||||
class MyEntity implements NotifyPropertyChanged
|
||||
{
|
||||
// ...
|
||||
|
||||
protected function _onPropertyChanged($propName, $oldValue, $newValue)
|
||||
{
|
||||
if ($this->_listeners) {
|
||||
foreach ($this->_listeners as $listener) {
|
||||
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function setData($data)
|
||||
{
|
||||
if ($data != $this->data) {
|
||||
$this->_onPropertyChanged('data', $this->data, $data);
|
||||
$this->data = $data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
You have to invoke `_onPropertyChanged` inside every method that changes the
|
||||
persistent state of `MyEntity`.
|
||||
|
||||
The check whether the new value is different from the old one is not mandatory
|
||||
but recommended. That way you also have full control over when you consider a
|
||||
property changed.
|
||||
|
||||
The negative point of this policy is obvious: You need implement an interface
|
||||
and write some plumbing code. But also note that we tried hard to keep this
|
||||
notification functionality abstract. Strictly speaking, it has nothing to do
|
||||
with the persistence layer and the Doctrine ORM or DBAL. You may find that
|
||||
property notification events come in handy in many other scenarios as well.
|
||||
As mentioned earlier, the `Doctrine\Common` namespace is not that evil and
|
||||
consists solely of very small classes and interfaces that have almost no
|
||||
external dependencies (none to the DBAL and none to the ORM) and that you
|
||||
can easily take with you should you want to swap out the persistence layer.
|
||||
This change tracking policy does not introduce a dependency on the Doctrine
|
||||
DBAL/ORM or the persistence layer.
|
||||
|
||||
The positive point and main advantage of this policy is its effectiveness. It
|
||||
has the best performance characteristics of the 3 policies with larger units of
|
||||
work and a flush() operation is very cheap when nothing has changed.
|
162
manual/en/limitations-and-known-issues.txt
Normal file
162
manual/en/limitations-and-known-issues.txt
Normal file
@ -0,0 +1,162 @@
|
||||
Much like every other piece of software Doctrine2 is not perfect and far from feature complete.
|
||||
This section should give you an overview of current limitations of Doctrine 2 as well as known issues that
|
||||
you should know about. We try to make using Doctrine2 a very pleasant experience. Therefore it is our believe
|
||||
that stating the limitations to our users as early as possible is very important.
|
||||
|
||||
The Known Issues section describes critical/blocker bugs and other issues that are either complicated to fix,
|
||||
not fixable due to backwards compatibility issues or where no simple fix exists (yet). We don't
|
||||
plan to add every bug in the tracker there, just those issues that can potentially cause nightmares
|
||||
or pain of any sort. Luckily this section is empty right now!
|
||||
|
||||
++ Current Limitations
|
||||
|
||||
There is a set of limitations that exist currently which might be solved in the future. Any of this
|
||||
limitations now stated has at least one ticket in the Tracker and is discussed for future releases.
|
||||
|
||||
+++ Foreign Keys as Identifiers
|
||||
|
||||
There are many use-cases where you would want to use an Entity-Attribute-Value approach to modelling and
|
||||
define a table-schema like the following:
|
||||
|
||||
[sql]
|
||||
CREATE TABLE product (
|
||||
id INTEGER,
|
||||
name VARCHAR,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE TABLE product_attributes (
|
||||
product_id INTEGER,
|
||||
attribute_name VARCHAR,
|
||||
attribute_value VARCHAR,
|
||||
PRIMARY KEY (product_id, attribute_name)
|
||||
);
|
||||
|
||||
This is currently *NOT* possible with Doctrine2. You have to define a surrogate key on the `product_attributes`
|
||||
table and use a unique-constraint for the `product_id` and `attribute_name`.
|
||||
|
||||
[sql]
|
||||
CREATE TABLE product_attributes (
|
||||
attribute_id, INTEGER,
|
||||
product_id INTEGER,
|
||||
attribute_name VARCHAR,
|
||||
attribute_value VARCHAR,
|
||||
PRIMARY KEY (attribute_id),
|
||||
UNIQUE (product_id, attribute_name)
|
||||
);
|
||||
|
||||
Although we state that we support composite primary keys that does not currently include foreign keys as primary key
|
||||
columns. To see the fundamental difference between the two different `product_attributes` tables you should see
|
||||
how they translate into a Doctrine Mapping (Using Annotations):
|
||||
|
||||
[php]
|
||||
/**
|
||||
* Scenario 1: THIS IS NOT POSSIBLE CURRENTLY
|
||||
* @Entity @Table(name="product_attributes")
|
||||
*/
|
||||
class ProductAttribute
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity="Product") */
|
||||
private $product;
|
||||
|
||||
/** @Id @Column(type="string", name="attribute_name") */
|
||||
private $name;
|
||||
|
||||
/** @Column(type="string", name="attribute_value") */
|
||||
private $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scenario 2: Using the surrogate key workaround
|
||||
* @Entity
|
||||
* @Table(name="product_attributes", uniqueConstraints={@UniqueConstraint(columns={"product_id", "attribute_name"})}))
|
||||
*/
|
||||
class ProductAttribute
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @ManyToOne(targetEntity="Product") */
|
||||
private $product;
|
||||
|
||||
/** @Column(type="string", name="attribute_name") */
|
||||
private $name;
|
||||
|
||||
/** @Column(type="string", name="attribute_value") */
|
||||
private $value;
|
||||
}
|
||||
|
||||
The following Jira Issue currently contains the feature request to allow @ManyToOne and @OneToOne annotations
|
||||
along the @Id annotation: [http://www.doctrine-project.org/jira/browse/DDC-117]
|
||||
|
||||
+++ Mapping Arrays to a Join Table
|
||||
|
||||
Related to the previous limitation with "Foreign Keys as Identifier" you might be interested in mapping the same
|
||||
table structure as given above to an array. However this is not yet possible either. See the following example:
|
||||
|
||||
[sql]
|
||||
CREATE TABLE product (
|
||||
id INTEGER,
|
||||
name VARCHAR,
|
||||
PRIMARY KEY(id)
|
||||
);
|
||||
|
||||
CREATE TABLE product_attributes (
|
||||
product_id INTEGER,
|
||||
attribute_name VARCHAR,
|
||||
attribute_value VARCHAR,
|
||||
PRIMARY KEY (product_id, attribute_name)
|
||||
);
|
||||
|
||||
This schema should be mapped to a Product Entity as follows:
|
||||
|
||||
class Product
|
||||
{
|
||||
private $id;
|
||||
private $name;
|
||||
private $attributes = array();
|
||||
}
|
||||
|
||||
Where the `attribute_name` column contains the key and `attribute_value` contains the value
|
||||
of each array element in `$attributes`.
|
||||
|
||||
The feature request for persistence of primitive value arrays [is described in the DDC-298 ticket](http://www.doctrine-project.org/jira/browse/DDC-298).
|
||||
|
||||
+++ Value Objects
|
||||
|
||||
There is currently no native support value objects in Doctrine other than for `DateTime` instances or if you
|
||||
serialize the objects using `serialize()/deserialize()` which the DBAL Type "object" supports.
|
||||
|
||||
The feature request for full value-object support [is described in the DDC-93 ticket](http://www.doctrine-project.org/jira/browse/DDC-93).
|
||||
|
||||
+++ Applying Filter Rules to any Query
|
||||
|
||||
There are scenarios in many applications where you want to apply additional filter rules to each query implicitly. Examples include:
|
||||
|
||||
* In I18N Applications restrict results to a entities annotated with a specific locale
|
||||
* For a large collection always only return objects in a specific date range/where condition applied.
|
||||
* Soft-Delete
|
||||
|
||||
There is currently no way to achieve this consistently across both DQL and Repository/Persister generated queries, but
|
||||
as this is a pretty important feature we plan to add support for it in the future.
|
||||
|
||||
+++ Custom Persisters
|
||||
|
||||
A Perister in Doctrine is an object that is responsible for the hydration and write operations of an entity against the database.
|
||||
Currently there is no way to overwrite the persister implementation for a given entity, however there are several use-cases that
|
||||
can benefit from custom persister implementations:
|
||||
|
||||
* [Add Upsert Support](http://www.doctrine-project.org/jira/browse/DDC-668)
|
||||
* [Evaluate possible ways in which stored-procedures can be used](http://www.doctrine-project.org/jira/browse/DDC-445)
|
||||
* The previous Filter Rules Feature Request
|
||||
|
||||
+++ Order of Collections
|
||||
|
||||
PHP Arrays are ordered hash-maps and so should be the `Doctrine\Common\Collections\Collection` interface. We plan
|
||||
to evaluate a feature that optionally persists and hydrates the keys of a Collection instance.
|
||||
|
||||
[Ticket DDC-213](http://www.doctrine-project.org/jira/browse/DDC-213)
|
||||
|
||||
++ Known Issues
|
||||
|
||||
There are currently no known critical/blocker or backward compatibility issues.
|
49
manual/en/partial-objects.txt
Normal file
49
manual/en/partial-objects.txt
Normal file
@ -0,0 +1,49 @@
|
||||
A partial object is an object whose state is not fully initialized after being
|
||||
reconstituted from the database and that is disconnected from the rest of its data.
|
||||
The following section will describe why partial objects are problematic and what the approach of Doctrine2 to this problem is.
|
||||
|
||||
> **NOTE**
|
||||
> The partial object problem in general does not apply to methods or
|
||||
> queries where you do not retrieve the query result as objects. Examples are:
|
||||
> `Query#getArrayResult()`, `Query#getScalarResult()`, `Query#getSingleScalarResult()`,
|
||||
> etc.
|
||||
|
||||
++ What is the problem?
|
||||
|
||||
In short, partial objects are problematic because they are usually objects with
|
||||
broken invariants. As such, code that uses these partial objects tends to be
|
||||
very fragile and either needs to "know" which fields or methods can be safely
|
||||
accessed or add checks around every field access or method invocation. The same
|
||||
holds true for the internals, i.e. the method implementations, of such objects.
|
||||
You usually simply assume the state you need in the method is available, after
|
||||
all you properly constructed this object before you pushed it into the database,
|
||||
right? These blind assumptions can quickly lead to null reference errors when
|
||||
working with such partial objects.
|
||||
|
||||
It gets worse with the scenario of an optional association (0..1 to 1). When
|
||||
the associated field is NULL, you dont know whether this object does not have
|
||||
an associated object or whether it was simply not loaded when the owning object
|
||||
was loaded from the database.
|
||||
|
||||
These are reasons why many ORMs do not allow partial objects at all and instead
|
||||
you always have to load an object with all its fields (associations being proxied).
|
||||
One secure way to allow partial objects is if the programming language/platform
|
||||
allows the ORM tool to hook deeply into the object and instrument it in such a
|
||||
way that individual fields (not only associations) can be loaded lazily on first
|
||||
access. This is possible in Java, for example, through bytecode instrumentation.
|
||||
In PHP though this is not possible, so there is no way to have "secure" partial
|
||||
objects in an ORM with transparent persistence.
|
||||
|
||||
Doctrine, by default, does not allow partial objects. That means, any query that
|
||||
only selects partial object data and wants to retrieve the result as objects
|
||||
(i.e. `Query#getResult()`) will raise an exception telling you that
|
||||
partial objects are dangerous. If you want to force a query to return you partial
|
||||
objects, possibly as a performance tweak, you can use the `partial` keyword as follows:
|
||||
|
||||
[php]
|
||||
$q = $em->createQuery("select partial u.{id,name} from MyApp\Domain\User u");
|
||||
|
||||
++ When should I force partial objects?
|
||||
|
||||
Mainly for optimization purposes, but be careful of premature optimization as partial objects
|
||||
lead to potentially more fragile code.
|
Loading…
Reference in New Issue
Block a user