init without trunk
This commit is contained in:
parent
ed24ac4994
commit
bb809e7233
14652 changed files with 177862 additions and 94817 deletions
375
vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst
vendored
Normal file
375
vendor/doctrine/orm/docs/en/tutorials/composite-primary-keys.rst
vendored
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
Composite and Foreign Keys as Primary Key
|
||||
=========================================
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
Doctrine 2 supports composite primary keys natively. Composite keys are a very powerful relational database concept
|
||||
and we took good care to make sure Doctrine 2 supports as many of the composite primary key use-cases.
|
||||
For Doctrine 2.0 composite keys of primitive data-types are supported, for Doctrine 2.1 even foreign keys as
|
||||
primary keys are supported.
|
||||
|
||||
This tutorial shows how the semantics of composite primary keys work and how they map to the database.
|
||||
|
||||
General Considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Every entity with a composite key cannot use an id generator other than "ASSIGNED". That means
|
||||
the ID fields have to have their values set before you call ``EntityManager#persist($entity)``.
|
||||
|
||||
Primitive Types only
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Even in version 2.0 you can have composite keys as long as they only consist of the primitive types
|
||||
``integer`` and ``string``. Suppose you want to create a database of cars and use the model-name
|
||||
and year of production as primary keys:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace VehicleCatalogue\Model;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Car
|
||||
{
|
||||
/** @Id @Column(type="string") */
|
||||
private $name;
|
||||
/** @Id @Column(type="integer") */
|
||||
private $year
|
||||
|
||||
public function __construct($name, $year)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->year = $year;
|
||||
}
|
||||
|
||||
public function getModelName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getYearOfProduction()
|
||||
{
|
||||
return $this->year;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="VehicleCatalogue\Model\Car">
|
||||
<id field="name" type="string" />
|
||||
<id field="year" type="integer" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
VehicleCatalogue\Model\Car:
|
||||
type: entity
|
||||
id:
|
||||
name:
|
||||
type: string
|
||||
year:
|
||||
type: integer
|
||||
|
||||
Now you can use this entity:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace VehicleCatalogue\Model;
|
||||
|
||||
// $em is the EntityManager
|
||||
|
||||
$car = new Car("Audi A8", 2010);
|
||||
$em->persist($car);
|
||||
$em->flush();
|
||||
|
||||
And for querying you can use arrays to both DQL and EntityRepositories:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace VehicleCatalogue\Model;
|
||||
|
||||
// $em is the EntityManager
|
||||
$audi = $em->find("VehicleCatalogue\Model\Car", array("name" => "Audi A8", "year" => 2010));
|
||||
|
||||
$dql = "SELECT c FROM VehicleCatalogue\Model\Car c WHERE c.id = ?1";
|
||||
$audi = $em->createQuery($dql)
|
||||
->setParameter(1, array("name" => "Audi A8", "year" => 2010))
|
||||
->getSingleResult();
|
||||
|
||||
You can also use this entity in associations. Doctrine will then generate two foreign keys one for ``name``
|
||||
and to ``year`` to the related entities.
|
||||
|
||||
.. note::
|
||||
|
||||
This example shows how you can nicely solve the requirement for existing
|
||||
values before ``EntityManager#persist()``: By adding them as mandatory values for the constructor.
|
||||
|
||||
Identity through foreign Entities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. note::
|
||||
|
||||
Identity through foreign entities is only supported with Doctrine 2.1
|
||||
|
||||
There are tons of use-cases where the identity of an Entity should be determined by the entity
|
||||
of one or many parent entities.
|
||||
|
||||
- Dynamic Attributes of an Entity (for example Article). Each Article has many
|
||||
attributes with primary key "article_id" and "attribute_name".
|
||||
- Address object of a Person, the primary key of the address is "user_id". This is not a case of a composite primary
|
||||
key, but the identity is derived through a foreign entity and a foreign key.
|
||||
- Join Tables with metadata can be modelled as Entity, for example connections between two articles
|
||||
with a little description and a score.
|
||||
|
||||
The semantics of mapping identity through foreign entities are easy:
|
||||
|
||||
- Only allowed on Many-To-One or One-To-One associations.
|
||||
- Plug an ``@Id`` annotation onto every association.
|
||||
- Set an attribute ``association-key`` with the field name of the association in XML.
|
||||
- Set a key ``associationKey:`` with the field name of the association in YAML.
|
||||
|
||||
Use-Case 1: Dynamic Attributes
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
We keep up the example of an Article with arbitrary attributes, the mapping looks like this:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace Application\Model;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Article
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
/** @Column(type="string") */
|
||||
private $title;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="ArticleAttribute", mappedBy="article", cascade={"ALL"}, indexBy="attribute")
|
||||
*/
|
||||
private $attributes;
|
||||
|
||||
public function addAttribute($name, $value)
|
||||
{
|
||||
$this->attributes[$name] = new ArticleAttribute($name, $value, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class ArticleAttribute
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity="Article", inversedBy="attributes") */
|
||||
private $article;
|
||||
|
||||
/** @Id @Column(type="string") */
|
||||
private $attribute;
|
||||
|
||||
/** @Column(type="string") */
|
||||
private $value;
|
||||
|
||||
public function __construct($name, $value, $article)
|
||||
{
|
||||
$this->attribute = $name;
|
||||
$this->value = $value;
|
||||
$this->article = $article;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Application\Model\ArticleAttribute">
|
||||
<id name="article" association-key="true" />
|
||||
<id name="attribute" type="string" />
|
||||
|
||||
<field name="value" type="string" />
|
||||
|
||||
<many-to-one field="article" target-entity="Article" inversed-by="attributes" />
|
||||
<entity>
|
||||
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Application\Model\ArticleAttribute:
|
||||
type: entity
|
||||
id:
|
||||
article:
|
||||
associationKey: true
|
||||
attribute:
|
||||
type: string
|
||||
fields:
|
||||
value:
|
||||
type: string
|
||||
manyToOne:
|
||||
article:
|
||||
targetEntity: Article
|
||||
inversedBy: attributes
|
||||
|
||||
|
||||
Use-Case 2: Simple Derived Identity
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sometimes you have the requirement that two objects are related by a One-To-One association
|
||||
and that the dependent class should re-use the primary key of the class it depends on.
|
||||
One good example for this is a user-address relationship:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
/** @Id @OneToOne(targetEntity="User") */
|
||||
private $user;
|
||||
}
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
|
||||
Address:
|
||||
type: entity
|
||||
id:
|
||||
user:
|
||||
associationKey: true
|
||||
oneToOne:
|
||||
user:
|
||||
targetEntity: User
|
||||
|
||||
|
||||
Use-Case 3: Join-Table with Metadata
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the classic order product shop example there is the concept of the order item
|
||||
which contains references to order and product and additional data such as the amount
|
||||
of products purchased and maybe even the current price.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/** @Entity */
|
||||
class Order
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @ManyToOne(targetEntity="Customer") */
|
||||
private $customer;
|
||||
/** @OneToMany(targetEntity="OrderItem", mappedBy="order") */
|
||||
private $items;
|
||||
|
||||
/** @Column(type="boolean") */
|
||||
private $payed = false;
|
||||
/** @Column(type="boolean") */
|
||||
private $shipped = false;
|
||||
/** @Column(type="datetime") */
|
||||
private $created;
|
||||
|
||||
public function __construct(Customer $customer)
|
||||
{
|
||||
$this->customer = $customer;
|
||||
$this->items = new ArrayCollection();
|
||||
$this->created = new \DateTime("now");
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Product
|
||||
{
|
||||
/** @Id @Column(type="integer") @GeneratedValue */
|
||||
private $id;
|
||||
|
||||
/** @Column(type="string") */
|
||||
private $name;
|
||||
|
||||
/** @Column(type="decimal") */
|
||||
private $currentPrice;
|
||||
|
||||
public function getCurrentPrice()
|
||||
{
|
||||
return $this->currentPrice;
|
||||
}
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class OrderItem
|
||||
{
|
||||
/** @Id @ManyToOne(targetEntity="Order") */
|
||||
private $order;
|
||||
|
||||
/** @Id @ManyToOne(targetEntity="Product") */
|
||||
private $product;
|
||||
|
||||
/** @Column(type="integer") */
|
||||
private $amount = 1;
|
||||
|
||||
/** @Column(type="decimal") */
|
||||
private $offeredPrice;
|
||||
|
||||
public function __construct(Order $order, Product $product, $amount = 1)
|
||||
{
|
||||
$this->order = $order;
|
||||
$this->product = $product;
|
||||
$this->offeredPrice = $product->getCurrentPrice();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Performance Considerations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Using composite keys always comes with a performance hit compared to using entities with
|
||||
a simple surrogate key. This performance impact is mostly due to additional PHP code that is
|
||||
necessary to handle this kind of keys, most notably when using derived identifiers.
|
||||
|
||||
On the SQL side there is not much overhead as no additional or unexpected queries have to be
|
||||
executed to manage entities with derived foreign keys.
|
||||
162
vendor/doctrine/orm/docs/en/tutorials/embeddables.rst
vendored
Normal file
162
vendor/doctrine/orm/docs/en/tutorials/embeddables.rst
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
Separating Concerns using Embeddables
|
||||
-------------------------------------
|
||||
|
||||
Embeddables are classes which are not entities themself, but are embedded
|
||||
in entities and can also be queried in DQL. You'll mostly want to use them
|
||||
to reduce duplication or separating concerns. Value objects such as date range
|
||||
or address are the primary use case for this feature. Embeddables can only
|
||||
contain properties with basic ``@Column`` mapping.
|
||||
|
||||
For the purposes of this tutorial, we will assume that you have a ``User``
|
||||
class in your application and you would like to store an address in
|
||||
the ``User`` class. We will model the ``Address`` class as an embeddable
|
||||
instead of simply adding the respective columns to the ``User`` class.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address") */
|
||||
private $address;
|
||||
}
|
||||
|
||||
/** @Embeddable */
|
||||
class Address
|
||||
{
|
||||
/** @Column(type = "string") */
|
||||
private $street;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private $postalCode;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private $city;
|
||||
|
||||
/** @Column(type = "string") */
|
||||
private $country;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" />
|
||||
</entity>
|
||||
|
||||
<embeddable name="Address">
|
||||
<field name="street" type="string" />
|
||||
<field name="postalCode" type="string" />
|
||||
<field name="city" type="string" />
|
||||
<field name="country" type="string" />
|
||||
</embeddable>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
|
||||
Address:
|
||||
type: embeddable
|
||||
fields:
|
||||
street: { type: string }
|
||||
postalCode: { type: string }
|
||||
city: { type: string }
|
||||
country: { type: string }
|
||||
|
||||
In terms of your database schema, Doctrine will automatically inline all
|
||||
columns from the ``Address`` class into the table of the ``User`` class,
|
||||
just as if you had declared them directly there.
|
||||
|
||||
Column Prefixing
|
||||
----------------
|
||||
|
||||
By default, Doctrine names your columns by prefixing them, using the value
|
||||
object name.
|
||||
|
||||
Following the example above, your columns would be named as ``address_street``,
|
||||
``address_postalCode``...
|
||||
|
||||
You can change this behaviour to meet your needs by changing the
|
||||
``columnPrefix`` attribute in the ``@Embedded`` notation.
|
||||
|
||||
The following example shows you how to set your prefix to ``myPrefix_``:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address", columnPrefix = "myPrefix_") */
|
||||
private $address;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" column-prefix="myPrefix_" />
|
||||
</entity>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
columnPrefix: myPrefix_
|
||||
|
||||
To have Doctrine drop the prefix and use the value object's property name
|
||||
directly, set ``columnPrefix=false`` (``use-column-prefix="false"`` for XML):
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
|
||||
/** @Entity */
|
||||
class User
|
||||
{
|
||||
/** @Embedded(class = "Address", columnPrefix = false) */
|
||||
private $address;
|
||||
}
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
embedded:
|
||||
address:
|
||||
class: Address
|
||||
columnPrefix: false
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<entity name="User">
|
||||
<embedded name="address" class="Address" use-column-prefix="false" />
|
||||
</entity>
|
||||
|
||||
|
||||
DQL
|
||||
---
|
||||
|
||||
You can also use mapped fields of embedded classes in DQL queries, just
|
||||
as if they were declared in the ``User`` class:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u FROM User u WHERE u.address.city = :myCity
|
||||
|
||||
86
vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst
vendored
Normal file
86
vendor/doctrine/orm/docs/en/tutorials/extra-lazy-associations.rst
vendored
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
Extra Lazy Associations
|
||||
=======================
|
||||
|
||||
.. versionadded:: 2.1
|
||||
|
||||
In many cases associations between entities can get pretty large. Even in a simple scenario like a blog.
|
||||
where posts can be commented, you always have to assume that a post draws hundreds of comments.
|
||||
In Doctrine 2.0 if you accessed an association it would always get loaded completely into memory. This
|
||||
can lead to pretty serious performance problems, if your associations contain several hundreds or thousands
|
||||
of entities.
|
||||
|
||||
With Doctrine 2.1 a feature called **Extra Lazy** is introduced for associations. Associations
|
||||
are marked as **Lazy** by default, which means the whole collection object for an association is populated
|
||||
the first time its accessed. If you mark an association as extra lazy the following methods on collections
|
||||
can be called without triggering a full load of the collection:
|
||||
|
||||
- ``Collection#contains($entity)``
|
||||
- ``Collection#containsKey($key)`` (available with Doctrine 2.5)
|
||||
- ``Collection#count()``
|
||||
- ``Collection#get($key)`` (available with Doctrine 2.4)
|
||||
- ``Collection#slice($offset, $length = null)``
|
||||
|
||||
For each of the above methods the following semantics apply:
|
||||
|
||||
- For each call, if the Collection is not yet loaded, issue a straight SELECT statement against the database.
|
||||
- For each call, if the collection is already loaded, fallback to the default functionality for lazy collections. No additional SELECT statements are executed.
|
||||
|
||||
Additionally even with Doctrine 2.0 the following methods do not trigger the collection load:
|
||||
|
||||
- ``Collection#add($entity)``
|
||||
- ``Collection#offsetSet($key, $entity)`` - ArrayAccess with no specific key ``$coll[] = $entity``, it does
|
||||
not work when setting specific keys like ``$coll[0] = $entity``.
|
||||
|
||||
With extra lazy collections you can now not only add entities to large collections but also paginate them
|
||||
easily using a combination of ``count`` and ``slice``.
|
||||
|
||||
|
||||
Enabling Extra-Lazy Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The mapping configuration is simple. Instead of using the default value of ``fetch="LAZY"`` you have to
|
||||
switch to extra lazy as shown in these examples:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace Doctrine\Tests\Models\CMS;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class CmsGroup
|
||||
{
|
||||
/**
|
||||
* @ManyToMany(targetEntity="CmsUser", mappedBy="groups", fetch="EXTRA_LAZY")
|
||||
*/
|
||||
public $users;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\Models\CMS\CmsGroup">
|
||||
<!-- ... -->
|
||||
<many-to-many field="users" target-entity="CmsUser" mapped-by="groups" fetch="EXTRA_LAZY" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Doctrine\Tests\Models\CMS\CmsGroup:
|
||||
type: entity
|
||||
# ...
|
||||
manyToMany:
|
||||
users:
|
||||
targetEntity: CmsUser
|
||||
mappedBy: groups
|
||||
fetch: EXTRA_LAZY
|
||||
|
||||
27
vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst
vendored
Normal file
27
vendor/doctrine/orm/docs/en/tutorials/getting-started-database.rst
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
Getting Started: Database First
|
||||
===============================
|
||||
|
||||
.. note:: *Development Workflows*
|
||||
|
||||
When you :doc:`Code First <getting-started>`, you
|
||||
start with developing Objects and then map them onto your database. When
|
||||
you :doc:`Model First <getting-started-models>`, you are modelling your application using tools (for
|
||||
example UML) and generate database schema and PHP code from this model.
|
||||
When you have a :doc:`Database First <getting-started-database>`, you already have a database schema
|
||||
and generate the corresponding PHP code from it.
|
||||
|
||||
.. note::
|
||||
|
||||
This getting started guide is in development.
|
||||
|
||||
Development of new applications often starts with an existing database schema.
|
||||
When the database schema is the starting point for your application, then
|
||||
development is said to use the *Database First* approach to Doctrine.
|
||||
|
||||
In this workflow you would modify the database schema first and then
|
||||
regenerate the PHP code to use with this schema. You need a flexible
|
||||
code-generator for this task and up to Doctrine 2.2, the code generator hasn't
|
||||
been flexible enough to achieve this.
|
||||
|
||||
We spinned off a subproject, Doctrine CodeGenerator, that will fill this gap and
|
||||
allow you to do *Database First* development.
|
||||
24
vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst
vendored
Normal file
24
vendor/doctrine/orm/docs/en/tutorials/getting-started-models.rst
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
Getting Started: Model First
|
||||
============================
|
||||
|
||||
.. note:: *Development Workflows*
|
||||
|
||||
When you :doc:`Code First <getting-started>`, you
|
||||
start with developing Objects and then map them onto your database. When
|
||||
you :doc:`Model First <getting-started-models>`, you are modelling your application using tools (for
|
||||
example UML) and generate database schema and PHP code from this model.
|
||||
When you have a :doc:`Database First <getting-started-database>`, then you already have a database schema
|
||||
and generate the corresponding PHP code from it.
|
||||
|
||||
.. note::
|
||||
|
||||
This getting started guide is in development.
|
||||
|
||||
There are applications when you start with a high-level description of the
|
||||
model using modelling tools such as UML. Modelling tools could also be Excel,
|
||||
XML or CSV files that describe the model in some structured way. If your
|
||||
application is using a modelling tool, then the development workflow is said to
|
||||
be a *Model First* approach to Doctrine2.
|
||||
|
||||
In this workflow you always change the model description and then regenerate
|
||||
both PHP code and database schema from this model.
|
||||
1549
vendor/doctrine/orm/docs/en/tutorials/getting-started.rst
vendored
Normal file
1549
vendor/doctrine/orm/docs/en/tutorials/getting-started.rst
vendored
Normal file
File diff suppressed because it is too large
Load diff
110
vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst
vendored
Normal file
110
vendor/doctrine/orm/docs/en/tutorials/ordered-associations.rst
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
Ordering To-Many Associations
|
||||
-----------------------------
|
||||
|
||||
There are use-cases when you'll want to sort collections when they are
|
||||
retrieved from the database. In userland you do this as long as you
|
||||
haven't initially saved an entity with its associations into the
|
||||
database. To retrieve a sorted collection from the database you can
|
||||
use the ``@OrderBy`` annotation with an collection that specifies
|
||||
an DQL snippet that is appended to all queries with this
|
||||
collection.
|
||||
|
||||
Additional to any ``@OneToMany`` or ``@ManyToMany`` annotation you
|
||||
can specify the ``@OrderBy`` in the following way:
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @Entity **/
|
||||
class User
|
||||
{
|
||||
// ...
|
||||
|
||||
/**
|
||||
* @ManyToMany(targetEntity="Group")
|
||||
* @OrderBy({"name" = "ASC"})
|
||||
**/
|
||||
private $groups;
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity name="User">
|
||||
<many-to-many field="groups" target-entity="Group">
|
||||
<order-by>
|
||||
<order-by-field name="name" direction="ASC" />
|
||||
</order-by>
|
||||
</many-to-many>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
User:
|
||||
type: entity
|
||||
manyToMany:
|
||||
groups:
|
||||
orderBy: { 'name': 'ASC' }
|
||||
targetEntity: Group
|
||||
joinTable:
|
||||
name: users_groups
|
||||
joinColumns:
|
||||
user_id:
|
||||
referencedColumnName: id
|
||||
inverseJoinColumns:
|
||||
group_id:
|
||||
referencedColumnName: id
|
||||
|
||||
The DQL Snippet in OrderBy is only allowed to consist of
|
||||
unqualified, unquoted field names and of an optional ASC/DESC
|
||||
positional statement. Multiple Fields are separated by a comma (,).
|
||||
The referenced field names have to exist on the ``targetEntity``
|
||||
class of the ``@ManyToMany`` or ``@OneToMany`` annotation.
|
||||
|
||||
The semantics of this feature can be described as follows.
|
||||
|
||||
|
||||
- ``@OrderBy`` acts as an implicit ORDER BY clause for the given
|
||||
fields, that is appended to all the explicitly given ORDER BY
|
||||
items.
|
||||
- All collections of the ordered type are always retrieved in an
|
||||
ordered fashion.
|
||||
- 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, the following would not add
|
||||
ORDER BY, since g is not fetch joined:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u FROM User u JOIN u.groups g WHERE SIZE(g) > 10
|
||||
|
||||
However the following:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10
|
||||
|
||||
...would internally be rewritten to:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name ASC
|
||||
|
||||
You can reverse the order with an explicit DQL ORDER BY:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC
|
||||
|
||||
...is internally rewritten to:
|
||||
|
||||
.. code-block:: sql
|
||||
|
||||
SELECT u, g FROM User u JOIN u.groups g WHERE u.id = 10 ORDER BY g.name DESC, g.name ASC
|
||||
|
||||
|
||||
90
vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst
vendored
Normal file
90
vendor/doctrine/orm/docs/en/tutorials/override-field-association-mappings-in-subclasses.rst
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
Override Field Association Mappings In Subclasses
|
||||
-------------------------------------------------
|
||||
|
||||
Sometimes there is a need to persist entities but override all or part of the
|
||||
mapping metadata. Sometimes also the mapping to override comes from entities
|
||||
using traits where the traits have mapping metadata.
|
||||
This tutorial explains how to override mapping metadata,
|
||||
i.e. attributes and associations metadata in particular. The example here shows
|
||||
the overriding of a class that uses a trait but is similar when extending a base
|
||||
class as shown at the end of this tutorial.
|
||||
|
||||
Suppose we have a class ExampleEntityWithOverride. This class uses trait ExampleTrait:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/**
|
||||
* @Entity
|
||||
*
|
||||
* @AttributeOverrides({
|
||||
* @AttributeOverride(name="foo",
|
||||
* column=@Column(
|
||||
* name = "foo_overridden",
|
||||
* type = "integer",
|
||||
* length = 140,
|
||||
* nullable = false,
|
||||
* unique = false
|
||||
* )
|
||||
* )
|
||||
* })
|
||||
*
|
||||
* @AssociationOverrides({
|
||||
* @AssociationOverride(name="bar",
|
||||
* joinColumns=@JoinColumn(
|
||||
* name="example_entity_overridden_bar_id", referencedColumnName="id"
|
||||
* )
|
||||
* )
|
||||
* })
|
||||
*/
|
||||
class ExampleEntityWithOverride
|
||||
{
|
||||
use ExampleTrait;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class Bar
|
||||
{
|
||||
/** @Id @Column(type="string") */
|
||||
private $id;
|
||||
}
|
||||
|
||||
The docblock is showing metadata override of the attribute and association type. It
|
||||
basically changes the names of the columns mapped for a property ``foo`` and for
|
||||
the association ``bar`` which relates to Bar class shown above. Here is the trait
|
||||
which has mapping metadata that is overridden by the annotation above:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/**
|
||||
* Trait class
|
||||
*/
|
||||
trait ExampleTrait
|
||||
{
|
||||
/** @Id @Column(type="string") */
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(name="trait_foo", type="integer", length=100, nullable=true, unique=true)
|
||||
*/
|
||||
protected $foo;
|
||||
|
||||
/**
|
||||
* @OneToOne(targetEntity="Bar", cascade={"persist", "merge"})
|
||||
* @JoinColumn(name="example_trait_bar_id", referencedColumnName="id")
|
||||
*/
|
||||
protected $bar;
|
||||
}
|
||||
|
||||
The case for just extending a class would be just the same but:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
class ExampleEntityWithOverride extends BaseEntityWithSomeMapping
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
Overriding is also supported via XML and YAML (:ref:`examples <inheritence_mapping_overrides>`).
|
||||
43
vendor/doctrine/orm/docs/en/tutorials/pagination.rst
vendored
Normal file
43
vendor/doctrine/orm/docs/en/tutorials/pagination.rst
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
Pagination
|
||||
==========
|
||||
|
||||
.. versionadded:: 2.2
|
||||
|
||||
Starting with version 2.2 Doctrine ships with a Paginator for DQL queries. It
|
||||
has a very simple API and implements the SPL interfaces ``Countable`` and
|
||||
``IteratorAggregate``.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
use Doctrine\ORM\Tools\Pagination\Paginator;
|
||||
|
||||
$dql = "SELECT p, c FROM BlogPost p JOIN p.comments c";
|
||||
$query = $entityManager->createQuery($dql)
|
||||
->setFirstResult(0)
|
||||
->setMaxResults(100);
|
||||
|
||||
$paginator = new Paginator($query, $fetchJoinCollection = true);
|
||||
|
||||
$c = count($paginator);
|
||||
foreach ($paginator as $post) {
|
||||
echo $post->getHeadline() . "\n";
|
||||
}
|
||||
|
||||
Paginating Doctrine queries is not as simple as you might think in the
|
||||
beginning. If you have complex fetch-join scenarios with one-to-many or
|
||||
many-to-many associations using the "default" LIMIT functionality of database
|
||||
vendors is not sufficient to get the correct results.
|
||||
|
||||
By default the pagination extension does the following steps to compute the
|
||||
correct result:
|
||||
|
||||
- Perform a Count query using `DISTINCT` keyword.
|
||||
- Perform a Limit Subquery with `DISTINCT` to find all ids of the entity in from on the current page.
|
||||
- Perform a WHERE IN query to get all results for the current page.
|
||||
|
||||
This behavior is only necessary if you actually fetch join a to-many
|
||||
collection. You can disable this behavior by setting the
|
||||
``$fetchJoinCollection`` flag to ``false``; in that case only 2 instead of the 3 queries
|
||||
described are executed. We hope to automate the detection for this in
|
||||
the future.
|
||||
298
vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst
vendored
Normal file
298
vendor/doctrine/orm/docs/en/tutorials/working-with-indexed-associations.rst
vendored
Normal file
|
|
@ -0,0 +1,298 @@
|
|||
Working with Indexed Associations
|
||||
=================================
|
||||
|
||||
.. note::
|
||||
|
||||
This feature is scheduled for version 2.1 of Doctrine and not included in the 2.0.x series.
|
||||
|
||||
Doctrine 2 collections are modelled after PHPs native arrays. PHP arrays are an ordered hashmap, but in
|
||||
the first version of Doctrine keys retrieved from the database were always numerical unless ``INDEX BY``
|
||||
was used. Starting with Doctrine 2.1 you can index your collections by a value in the related entity.
|
||||
This is a first step towards full ordered hashmap support through the Doctrine ORM.
|
||||
The feature works like an implicit ``INDEX BY`` for the selected association but has several
|
||||
downsides also:
|
||||
|
||||
- You have to manage both the key and field if you want to change the index by field value.
|
||||
- On each request the keys are regenerated from the field value not from the previous collection key.
|
||||
- Values of the Index-By keys are never considered during persistence, it only exists for accessing purposes.
|
||||
- Fields that are used for the index by feature **HAVE** to be unique in the database. The behavior for multiple entities
|
||||
with the same index-by field value is undefined.
|
||||
|
||||
As an example we will design a simple stock exchange list view. The domain consists of the entity ``Stock``
|
||||
and ``Market`` where each Stock has a symbol and is traded on a single market. Instead of having a numerical
|
||||
list of stocks traded on a market they will be indexed by their symbol, which is unique across all markets.
|
||||
|
||||
Mapping Indexed Associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
You can map indexed associations by adding:
|
||||
|
||||
* ``indexBy`` attribute to any ``@OneToMany`` or ``@ManyToMany`` annotation.
|
||||
* ``index-by`` attribute to any ``<one-to-many />`` or ``<many-to-many />`` xml element.
|
||||
* ``indexBy:`` key-value pair to any association defined in ``manyToMany:`` or ``oneToMany:`` YAML mapping files.
|
||||
|
||||
The code and mappings for the Market entity looks like this:
|
||||
|
||||
.. configuration-block::
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace Doctrine\Tests\Models\StockExchange;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="exchange_markets")
|
||||
*/
|
||||
class Market
|
||||
{
|
||||
/**
|
||||
* @Id @Column(type="integer") @GeneratedValue
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @Column(type="string")
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Stock", mappedBy="market", indexBy="symbol")
|
||||
* @var Stock[]
|
||||
*/
|
||||
private $stocks;
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
$this->stocks = new ArrayCollection();
|
||||
}
|
||||
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function addStock(Stock $stock)
|
||||
{
|
||||
$this->stocks[$stock->getSymbol()] = $stock;
|
||||
}
|
||||
|
||||
public function getStock($symbol)
|
||||
{
|
||||
if (!isset($this->stocks[$symbol])) {
|
||||
throw new \InvalidArgumentException("Symbol is not traded on this market.");
|
||||
}
|
||||
|
||||
return $this->stocks[$symbol];
|
||||
}
|
||||
|
||||
public function getStocks()
|
||||
{
|
||||
return $this->stocks->toArray();
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\Models\StockExchange\Market">
|
||||
<id name="id" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="name" type="string"/>
|
||||
|
||||
<one-to-many target-entity="Stock" mapped-by="market" field="stocks" index-by="symbol" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Doctrine\Tests\Models\StockExchange\Market:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
name:
|
||||
type:string
|
||||
oneToMany:
|
||||
stocks:
|
||||
targetEntity: Stock
|
||||
mappedBy: market
|
||||
indexBy: symbol
|
||||
|
||||
Inside the ``addStock()`` method you can see how we directly set the key of the association to the symbol,
|
||||
so that we can work with the indexed association directly after invoking ``addStock()``. Inside ``getStock($symbol)``
|
||||
we pick a stock traded on the particular market by symbol. If this stock doesn't exist an exception is thrown.
|
||||
|
||||
The ``Stock`` entity doesn't contain any special instructions that are new, but for completeness
|
||||
here are the code and mappings for it:
|
||||
|
||||
.. configuration-block::
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
namespace Doctrine\Tests\Models\StockExchange;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="exchange_stocks")
|
||||
*/
|
||||
class Stock
|
||||
{
|
||||
/**
|
||||
* @Id @GeneratedValue @Column(type="integer")
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* For real this column would have to be unique=true. But I want to test behavior of non-unique overrides.
|
||||
*
|
||||
* @Column(type="string", unique=true)
|
||||
*/
|
||||
private $symbol;
|
||||
|
||||
/**
|
||||
* @ManyToOne(targetEntity="Market", inversedBy="stocks")
|
||||
* @var Market
|
||||
*/
|
||||
private $market;
|
||||
|
||||
public function __construct($symbol, Market $market)
|
||||
{
|
||||
$this->symbol = $symbol;
|
||||
$this->market = $market;
|
||||
$market->addStock($this);
|
||||
}
|
||||
|
||||
public function getSymbol()
|
||||
{
|
||||
return $this->symbol;
|
||||
}
|
||||
}
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
|
||||
http://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
|
||||
|
||||
<entity name="Doctrine\Tests\Models\StockExchange\Stock">
|
||||
<id name="id" type="integer">
|
||||
<generator strategy="AUTO" />
|
||||
</id>
|
||||
|
||||
<field name="symbol" type="string" unique="true" />
|
||||
<many-to-one target-entity="Market" field="market" inversed-by="stocks" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
Doctrine\Tests\Models\StockExchange\Stock:
|
||||
type: entity
|
||||
id:
|
||||
id:
|
||||
type: integer
|
||||
generator:
|
||||
strategy: AUTO
|
||||
fields:
|
||||
symbol:
|
||||
type: string
|
||||
manyToOne:
|
||||
market:
|
||||
targetEntity: Market
|
||||
inversedBy: stocks
|
||||
|
||||
Querying indexed associations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Now that we defined the stocks collection to be indexed by symbol we can take a look at some code,
|
||||
that makes use of the indexing.
|
||||
|
||||
First we will populate our database with two example stocks traded on a single market:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// $em is the EntityManager
|
||||
|
||||
$market = new Market("Some Exchange");
|
||||
$stock1 = new Stock("AAPL", $market);
|
||||
$stock2 = new Stock("GOOG", $market);
|
||||
|
||||
$em->persist($market);
|
||||
$em->persist($stock1);
|
||||
$em->persist($stock2);
|
||||
$em->flush();
|
||||
|
||||
This code is not particular interesting since the indexing feature is not yet used. In a new request we could
|
||||
now query for the market:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// $em is the EntityManager
|
||||
$marketId = 1;
|
||||
$symbol = "AAPL";
|
||||
|
||||
$market = $em->find("Doctrine\Tests\Models\StockExchange\Market", $marketId);
|
||||
|
||||
// Access the stocks by symbol now:
|
||||
$stock = $market->getStock($symbol);
|
||||
|
||||
echo $stock->getSymbol(); // will print "AAPL"
|
||||
|
||||
The implementation ``Market::addStock()`` in combination with ``indexBy`` allows to access the collection
|
||||
consistently by the Stock symbol. It does not matter if Stock is managed by Doctrine or not.
|
||||
|
||||
The same applies to DQL queries: The ``indexBy`` configuration acts as implicit "INDEX BY" to a join association.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// $em is the EntityManager
|
||||
$marketId = 1;
|
||||
$symbol = "AAPL";
|
||||
|
||||
$dql = "SELECT m, s FROM Doctrine\Tests\Models\StockExchange\Market m JOIN m.stocks s WHERE m.id = ?1";
|
||||
$market = $em->createQuery($dql)
|
||||
->setParameter(1, $marketId)
|
||||
->getSingleResult();
|
||||
|
||||
// Access the stocks by symbol now:
|
||||
$stock = $market->getStock($symbol);
|
||||
|
||||
echo $stock->getSymbol(); // will print "AAPL"
|
||||
|
||||
If you want to use ``INDEX BY`` explicitly on an indexed association you are free to do so. Additionally
|
||||
indexed associations also work with the ``Collection::slice()`` functionality, no matter if marked as
|
||||
LAZY or EXTRA_LAZY.
|
||||
|
||||
Outlook into the Future
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For the inverse side of a many-to-many associations there will be a way to persist the keys and the order
|
||||
as a third and fourth parameter into the join table. This feature is discussed in `DDC-213 <http://www.doctrine-project.org/jira/browse/DDC-213>`_
|
||||
This feature cannot be implemented for One-To-Many associations, because they are never the owning side.
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue