Merge additional fix (and master changes) from taueres/fix-instance-of-subclasses
This commit is contained in:
commit
c7ef9085fb
@ -1,34 +1,24 @@
|
||||
build:
|
||||
environment:
|
||||
php:
|
||||
version: 7.1
|
||||
|
||||
before_commands:
|
||||
- "composer install --no-dev --prefer-source"
|
||||
|
||||
tools:
|
||||
external_code_coverage:
|
||||
timeout: 3600
|
||||
php_code_coverage:
|
||||
enabled: true
|
||||
php_code_sniffer:
|
||||
enabled: false
|
||||
php_cpd:
|
||||
enabled: true
|
||||
excluded_dirs: ["bin", "docs", "tests", "tools", "vendor"]
|
||||
php_cs_fixer:
|
||||
enabled: false
|
||||
php_loc:
|
||||
enabled: true
|
||||
excluded_dirs: ["bin", "docs", "tests", "tools", "vendor"]
|
||||
php_mess_detector:
|
||||
enabled: true
|
||||
filter:
|
||||
paths: ["lib/*"]
|
||||
php_pdepend:
|
||||
enabled: true
|
||||
excluded_dirs: ["docs", "examples", "tests", "vendor"]
|
||||
php_analyzer:
|
||||
enabled: true
|
||||
filter:
|
||||
paths: ["lib/*", "tests/*"]
|
||||
php_hhvm:
|
||||
enabled: true
|
||||
filter:
|
||||
paths: ["lib/*", "tests/*"]
|
||||
sensiolabs_security_checker: true
|
||||
|
||||
filter:
|
||||
excluded_paths:
|
||||
- docs
|
||||
- tools
|
||||
|
||||
build_failure_conditions:
|
||||
- 'elements.rating(<= C).new.exists' # No new classes/methods with a rating of C or worse allowed
|
||||
- 'issues.label("coding-style").new.exists' # No new coding style issues allowed
|
||||
- 'issues.severity(>= MAJOR).new.exists' # New issues of major or higher severity
|
||||
- 'project.metric_change("scrutinizer.test_coverage", < 0)' # Code Coverage decreased from previous inspection
|
||||
- 'patches.label("Doc Comments").new.exists' # No new doc comments patches allowed
|
||||
- 'patches.label("Unused Use Statements").new.exists' # No new unused imports patches allowed
|
||||
|
49
.travis.yml
49
.travis.yml
@ -1,11 +1,10 @@
|
||||
dist: trusty
|
||||
sudo: false
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 7.0
|
||||
- 7.1
|
||||
- nightly
|
||||
- hhvm
|
||||
|
||||
env:
|
||||
- DB=mysql
|
||||
@ -13,28 +12,25 @@ env:
|
||||
- DB=sqlite
|
||||
|
||||
before_script:
|
||||
- if [[ $TRAVIS_PHP_VERSION = '7.0' && $DB = 'sqlite' ]]; then PHPUNIT_FLAGS="--coverage-clover ./build/logs/clover.xml"; else PHPUNIT_FLAGS=""; fi
|
||||
- if [[ $TRAVIS_PHP_VERSION -lt '7.0' && $TRAVIS_PHP_VERSION != 'hhv*' ]]; then phpenv config-rm xdebug.ini; fi
|
||||
- if [[ $TRAVIS_PHP_VERSION = '7.1' && $DB = 'sqlite' && "$DEPENDENCIES" != "low" ]]; then PHPUNIT_FLAGS="--coverage-clover ./build/logs/clover.xml"; else PHPUNIT_FLAGS=""; fi
|
||||
- if [[ "$PHPUNIT_FLAGS" == "" ]]; then phpenv config-rm xdebug.ini; fi
|
||||
- composer self-update
|
||||
- composer install --prefer-source
|
||||
- if [ "$DEPENDENCIES" != "low" ]; then composer update; fi;
|
||||
- if [ "$DEPENDENCIES" == "low" ]; then composer update --prefer-lowest; fi;
|
||||
- if [[ $DB == "mysql" || $DB == "mariadb" ]]; then mysql -e "CREATE SCHEMA doctrine_tests; GRANT ALL PRIVILEGES ON doctrine_tests.* to travis@'%'"; fi;
|
||||
|
||||
script:
|
||||
- ENABLE_SECOND_LEVEL_CACHE=0 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml $PHPUNIT_FLAGS
|
||||
- ENABLE_SECOND_LEVEL_CACHE=1 ./vendor/bin/phpunit -v -c tests/travis/$DB.travis.xml --exclude-group performance,non-cacheable,locking_functional
|
||||
|
||||
after_script:
|
||||
- if [[ $TRAVIS_PHP_VERSION = '7.0' && $DB = 'sqlite' ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
|
||||
- if [[ $TRAVIS_PHP_VERSION = '7.0' && $DB = 'sqlite' ]]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml; fi
|
||||
- if [[ "$PHPUNIT_FLAGS" != "" ]]; then wget https://scrutinizer-ci.com/ocular.phar; fi
|
||||
- if [[ "$PHPUNIT_FLAGS" != "" ]]; then php ocular.phar code-coverage:upload --format=php-clover build/logs/clover.xml; fi
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- php: 7.0
|
||||
env: DB=mariadb
|
||||
addons:
|
||||
mariadb: 10.1
|
||||
- php: 7.1
|
||||
env: DB=mariadb
|
||||
addons:
|
||||
@ -43,42 +39,9 @@ matrix:
|
||||
env:
|
||||
- DB=sqlite
|
||||
- DEPENDENCIES='low'
|
||||
- php: hhvm
|
||||
sudo: true
|
||||
dist: trusty
|
||||
group: edge # until the next Trusty update
|
||||
addons:
|
||||
mariadb: 10.1
|
||||
env: DB=mariadb
|
||||
- php: hhvm
|
||||
sudo: true
|
||||
dist: trusty
|
||||
group: edge # until the next update
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- mysql-server-5.6
|
||||
- mysql-client-core-5.6
|
||||
- mysql-client-5.6
|
||||
services:
|
||||
- mysql
|
||||
env: DB=mysql
|
||||
- php: hhvm
|
||||
sudo: true
|
||||
dist: trusty
|
||||
group: edge # until the next update
|
||||
services:
|
||||
- postgresql
|
||||
env: DB=pgsql
|
||||
- php: hhvm
|
||||
sudo: true
|
||||
dist: trusty
|
||||
group: edge # until the next update
|
||||
env: DB=sqlite
|
||||
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
- php: hhvm
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
19
UPGRADE.md
19
UPGRADE.md
@ -1,3 +1,22 @@
|
||||
# Upgrade to 2.6
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\Query\Parser#isInternalFunction`
|
||||
|
||||
Method `Doctrine\ORM\Query\QueryException::associationPathInverseSideNotSupported`
|
||||
now has a required parameter `$pathExpr`.
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\Query\Parser#isInternalFunction`
|
||||
|
||||
Method `Doctrine\ORM\Query\Parser#isInternalFunction` was removed because
|
||||
the distinction between internal function and user defined DQL was removed.
|
||||
[#6500](https://github.com/doctrine/doctrine2/pull/6500)
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\ORMException#overwriteInternalDQLFunctionNotAllowed`
|
||||
|
||||
Method `Doctrine\ORM\Query\Parser#overwriteInternalDQLFunctionNotAllowed` was
|
||||
removed because of the choice to allow users to overwrite internal functions, ie
|
||||
`AVG`, `SUM`, `COUNT`, `MIN` and `MAX`. [#6500](https://github.com/doctrine/doctrine2/pull/6500)
|
||||
|
||||
# Upgrade to 2.5
|
||||
|
||||
## Minor BC BREAK: removed `Doctrine\ORM\Query\SqlWalker#walkCaseExpression()`
|
||||
|
@ -14,18 +14,18 @@
|
||||
],
|
||||
"minimum-stability": "dev",
|
||||
"require": {
|
||||
"php": "^7.0",
|
||||
"php": "^7.1",
|
||||
"ext-pdo": "*",
|
||||
"doctrine/collections": "~1.3",
|
||||
"doctrine/collections": "^1.4",
|
||||
"doctrine/dbal": ">=2.5-dev,<2.7-dev",
|
||||
"doctrine/instantiator": "~1.0.1",
|
||||
"doctrine/common": "^2.7.1",
|
||||
"doctrine/cache": "~1.5",
|
||||
"doctrine/annotations": "~1.2",
|
||||
"symfony/console": "~2.5|~3.0"
|
||||
"doctrine/cache": "~1.6",
|
||||
"doctrine/annotations": "~1.4",
|
||||
"symfony/console": "~3.0|~4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/yaml": "~2.3|~3.0",
|
||||
"symfony/yaml": "~3.0|~4.0",
|
||||
"phpunit/phpunit": "^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit dc294be1dbcf9abde82d24aea1902898d6ee6314
|
||||
Subproject commit 6f1bc8bead17b8032389659c0b071d00f2c58328
|
@ -132,7 +132,7 @@ dql statement.
|
||||
|
||||
The ``ArithmeticPrimary`` method call is the most common
|
||||
denominator of valid EBNF tokens taken from the
|
||||
`DQL EBNF grammar <http://www.doctrine-project.org/documentation/manual/2_0/en/dql-doctrine-query-language#ebnf>`_
|
||||
`DQL EBNF grammar <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#ebnf>`_
|
||||
that matches our requirements for valid input into the DateDiff Dql
|
||||
function. Picking the right tokens for your methods is a tricky
|
||||
business, but the EBNF grammar is pretty helpful finding it, as is
|
||||
|
@ -4,7 +4,7 @@ Implementing Wakeup or Clone
|
||||
.. sectionauthor:: Roman Borschel (roman@code-factory.org)
|
||||
|
||||
As explained in the
|
||||
`restrictions for entity classes in the manual <http://www.doctrine-project.org/documentation/manual/2_0/en/architecture#entities>`_,
|
||||
`restrictions for entity classes in the manual <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/architecture.html#entities>`_,
|
||||
it is usually not allowed for an entity to implement ``__wakeup``
|
||||
or ``__clone``, because Doctrine makes special use of them.
|
||||
However, it is quite easy to make use of these methods in a safe
|
||||
|
@ -111,7 +111,7 @@ APC, get rid of EchoSqlLogger, and turn off
|
||||
autoGenerateProxyClasses.
|
||||
|
||||
For more details, consult the
|
||||
`Doctrine 2 Configuration documentation <http://www.doctrine-project.org/documentation/manual/2_0/en/configuration#configuration-options>`_.
|
||||
`Doctrine 2 Configuration documentation <http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/configuration.html>`_.
|
||||
|
||||
Now to use it
|
||||
-------------
|
||||
|
@ -24,6 +24,9 @@ One tip for working with relations is to read the relation from left to right, w
|
||||
|
||||
See below for all the possible relations.
|
||||
|
||||
An association is considered to be unidirectional if only one side of the association has
|
||||
a property referring to the other side.
|
||||
|
||||
To gain a full understanding of associations you should also read about :doc:`owning and
|
||||
inverse sides of associations <unitofwork-associations>`
|
||||
|
||||
@ -105,9 +108,7 @@ One-To-One, Unidirectional
|
||||
--------------------------
|
||||
|
||||
Here is an example of a one-to-one association with a ``Product`` entity that
|
||||
references one ``Shipping`` entity. The ``Shipping`` does not reference back to
|
||||
the ``Product`` so that the reference is said to be unidirectional, in one
|
||||
direction only.
|
||||
references one ``Shipment`` entity.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -120,17 +121,17 @@ direction only.
|
||||
// ...
|
||||
|
||||
/**
|
||||
* One Product has One Shipping.
|
||||
* @OneToOne(targetEntity="Shipping")
|
||||
* @JoinColumn(name="shipping_id", referencedColumnName="id")
|
||||
* One Product has One Shipment.
|
||||
* @OneToOne(targetEntity="Shipment")
|
||||
* @JoinColumn(name="shipment_id", referencedColumnName="id")
|
||||
*/
|
||||
private $shipping;
|
||||
private $shipment;
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
/** @Entity */
|
||||
class Shipping
|
||||
class Shipment
|
||||
{
|
||||
// ...
|
||||
}
|
||||
@ -139,8 +140,8 @@ direction only.
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipping" target-entity="Shipping">
|
||||
<join-column name="shipping_id" referenced-column-name="id" />
|
||||
<one-to-one field="shipment" target-entity="Shipment">
|
||||
<join-column name="shipment_id" referenced-column-name="id" />
|
||||
</one-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@ -150,10 +151,10 @@ direction only.
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipping:
|
||||
targetEntity: Shipping
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
joinColumn:
|
||||
name: shipping_id
|
||||
name: shipment_id
|
||||
referencedColumnName: id
|
||||
|
||||
Note that the @JoinColumn is not really necessary in this example,
|
||||
@ -165,15 +166,15 @@ Generated MySQL Schema:
|
||||
|
||||
CREATE TABLE Product (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
shipping_id INT DEFAULT NULL,
|
||||
UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipping_id),
|
||||
shipment_id INT DEFAULT NULL,
|
||||
UNIQUE INDEX UNIQ_6FBC94267FE4B2B (shipment_id),
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
CREATE TABLE Shipping (
|
||||
CREATE TABLE Shipment (
|
||||
id INT AUTO_INCREMENT NOT NULL,
|
||||
PRIMARY KEY(id)
|
||||
) ENGINE = InnoDB;
|
||||
ALTER TABLE Product ADD FOREIGN KEY (shipping_id) REFERENCES Shipping(id);
|
||||
ALTER TABLE Product ADD FOREIGN KEY (shipment_id) REFERENCES Shipment(id);
|
||||
|
||||
One-To-One, Bidirectional
|
||||
-------------------------
|
||||
@ -182,6 +183,10 @@ Here is a one-to-one relationship between a ``Customer`` and a
|
||||
``Cart``. The ``Cart`` has a reference back to the ``Customer`` so
|
||||
it is bidirectional.
|
||||
|
||||
Here we see the ``mappedBy`` and ``inversedBy`` annotations for the first time.
|
||||
They are used to tell Doctrine which property on the other side refers to the
|
||||
object.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
.. code-block:: php
|
||||
@ -263,8 +268,9 @@ Generated MySQL Schema:
|
||||
) ENGINE = InnoDB;
|
||||
ALTER TABLE Cart ADD FOREIGN KEY (customer_id) REFERENCES Customer(id);
|
||||
|
||||
See how the foreign key is defined on the owning side of the
|
||||
relation, the table ``Cart``.
|
||||
We had a choice of sides on which to place the ``mappedBy`` attribute. Because it
|
||||
is on the ``Cart``, that is the owning side of the relation, and thus holds the
|
||||
foreign key.
|
||||
|
||||
One-To-One, Self-referencing
|
||||
----------------------------
|
||||
@ -307,15 +313,16 @@ With the generated MySQL Schema:
|
||||
One-To-Many, Bidirectional
|
||||
--------------------------
|
||||
|
||||
A one-to-many association has to be bidirectional, unless you are using an
|
||||
additional join-table. This is necessary, because of the foreign key
|
||||
in a one-to-many association being defined on the "many" side. Doctrine
|
||||
needs a many-to-one association that defines the mapping of this
|
||||
foreign key.
|
||||
A one-to-many association has to be bidirectional, unless you are using a
|
||||
join table. This is because the many side in a one-to-many association holds
|
||||
the foreign key, making it the owning side. Doctrine needs the many side
|
||||
defined in order to understand the association.
|
||||
|
||||
This bidirectional mapping requires the ``mappedBy`` attribute on the
|
||||
``OneToMany`` association and the ``inversedBy`` attribute on the ``ManyToOne``
|
||||
association.
|
||||
"one" side and the ``inversedBy`` attribute on the "many" side.
|
||||
|
||||
This means there is no difference between a bidirectional one-to-many and a
|
||||
bidirectional many-to-one.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -775,14 +782,14 @@ one is bidirectional.
|
||||
The MySQL schema is exactly the same as for the Many-To-Many
|
||||
uni-directional case above.
|
||||
|
||||
Owning and Inverse Side on a ManyToMany association
|
||||
Owning and Inverse Side on a ManyToMany Association
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For Many-To-Many associations you can chose which entity is the
|
||||
owning and which the inverse side. There is a very simple semantic
|
||||
rule to decide which side is more suitable to be the owning side
|
||||
from a developers perspective. You only have to ask yourself, which
|
||||
entity is responsible for the connection management and pick that
|
||||
from a developers perspective. You only have to ask yourself which
|
||||
entity is responsible for the connection management, and pick that
|
||||
as the owning side.
|
||||
|
||||
Take an example of two entities ``Article`` and ``Tag``. Whenever
|
||||
@ -790,7 +797,7 @@ you want to connect an Article to a Tag and vice-versa, it is
|
||||
mostly the Article that is responsible for this relation. Whenever
|
||||
you add a new article, you want to connect it with existing or new
|
||||
tags. Your create Article form will probably support this notion
|
||||
and allow to specify the tags directly. This is why you should pick
|
||||
and allow specifying the tags directly. This is why you should pick
|
||||
the Article as owning side, as it makes the code more
|
||||
understandable:
|
||||
|
||||
@ -906,14 +913,14 @@ As an example, consider this mapping:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
/** @OneToOne(targetEntity="Shipping") */
|
||||
private $shipping;
|
||||
/** @OneToOne(targetEntity="Shipment") */
|
||||
private $shipment;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipping" target-entity="Shipping" />
|
||||
<one-to-one field="shipment" target-entity="Shipment" />
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
|
||||
@ -922,8 +929,8 @@ As an example, consider this mapping:
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipping:
|
||||
targetEntity: Shipping
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
|
||||
This is essentially the same as the following, more verbose,
|
||||
mapping:
|
||||
@ -934,18 +941,18 @@ mapping:
|
||||
|
||||
<?php
|
||||
/**
|
||||
* One Product has One Shipping.
|
||||
* @OneToOne(targetEntity="Shipping")
|
||||
* @JoinColumn(name="shipping_id", referencedColumnName="id")
|
||||
* One Product has One Shipment.
|
||||
* @OneToOne(targetEntity="Shipment")
|
||||
* @JoinColumn(name="shipment_id", referencedColumnName="id")
|
||||
*/
|
||||
private $shipping;
|
||||
private $shipment;
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<doctrine-mapping>
|
||||
<entity class="Product">
|
||||
<one-to-one field="shipping" target-entity="Shipping">
|
||||
<join-column name="shipping_id" referenced-column-name="id" />
|
||||
<one-to-one field="shipment" target-entity="Shipment">
|
||||
<join-column name="shipment_id" referenced-column-name="id" />
|
||||
</one-to-one>
|
||||
</entity>
|
||||
</doctrine-mapping>
|
||||
@ -955,10 +962,10 @@ mapping:
|
||||
Product:
|
||||
type: entity
|
||||
oneToOne:
|
||||
shipping:
|
||||
targetEntity: Shipping
|
||||
shipment:
|
||||
targetEntity: Shipment
|
||||
joinColumn:
|
||||
name: shipping_id
|
||||
name: shipment_id
|
||||
referencedColumnName: id
|
||||
|
||||
The @JoinTable definition used for many-to-many mappings has
|
||||
|
@ -1380,7 +1380,13 @@ Given that there are 10 users and corresponding addresses in the database the ex
|
||||
SELECT * FROM address WHERE id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
|
||||
|
||||
.. note::
|
||||
Changing the fetch mode during a query is only possible for one-to-one and many-to-one relations.
|
||||
Changing the fetch mode during a query mostly makes sense for one-to-one and many-to-one relations. In that case,
|
||||
all the necessary IDs are available after the root entity (``user`` in the above example) has been loaded. So, one
|
||||
query per association can be executed to fetch all the referred-to entities (``address``).
|
||||
|
||||
For one-to-many relations, changing the fetch mode to eager will cause to execute one query **for every root entity
|
||||
loaded**. This gives no improvement over the ``lazy`` fetch mode which will also initialize the associations on
|
||||
a one-by-one basis once they are accessed.
|
||||
|
||||
|
||||
EBNF
|
||||
|
@ -455,6 +455,7 @@ Things to note:
|
||||
- The association type *CANNOT* be changed.
|
||||
- The override could redefine the joinTables or joinColumns depending on the association type.
|
||||
- The override could redefine inversedBy to reference more than one extended entity.
|
||||
- The override could redefine fetch to modify the fetch strategy of the extended entity.
|
||||
|
||||
Attribute Override
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -20,7 +20,7 @@ You can specify a different strategy by calling ``Doctrine\ORM\Configuration#set
|
||||
|
||||
<?php
|
||||
$namingStrategy = new MyNamingStrategy();
|
||||
$configuration()->setNamingStrategy($namingStrategy);
|
||||
$configuration->setNamingStrategy($namingStrategy);
|
||||
|
||||
Underscore naming strategy
|
||||
---------------------------
|
||||
@ -31,7 +31,7 @@ Underscore naming strategy
|
||||
|
||||
<?php
|
||||
$namingStrategy = new \Doctrine\ORM\Mapping\UnderscoreNamingStrategy(CASE_UPPER);
|
||||
$configuration()->setNamingStrategy($namingStrategy);
|
||||
$configuration->setNamingStrategy($namingStrategy);
|
||||
|
||||
For SomeEntityName the strategy will generate the table SOME_ENTITY_NAME with the
|
||||
``CASE_UPPER`` option, or some_entity_name with the ``CASE_LOWER`` option.
|
||||
|
@ -126,7 +126,7 @@ Here is a complete list of helper methods available in ``QueryBuilder``:
|
||||
// Example - $qb->select(array('u', 'p'))
|
||||
// Example - $qb->select($qb->expr()->select('u', 'p'))
|
||||
public function select($select = null);
|
||||
|
||||
|
||||
// addSelect does not override previous calls to select
|
||||
//
|
||||
// Example - $qb->select('u');
|
||||
@ -580,4 +580,3 @@ same query of example 6 written using
|
||||
Of course this is the hardest way to build a DQL query in Doctrine.
|
||||
To simplify some of these efforts, we introduce what we call as
|
||||
``Expr`` helper class.
|
||||
|
||||
|
@ -97,7 +97,7 @@ Defines a contract for accessing a particular region.
|
||||
|
||||
Defines a contract for accessing a particular cache region.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.Region.html/>`_.
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.Region.html>`_.
|
||||
|
||||
Concurrent cache region
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -111,7 +111,7 @@ If you want to use an ``READ_WRITE`` cache, you should consider providing your o
|
||||
|
||||
Defines contract for concurrently managed data region.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.ConcurrentRegion.html/>`_.
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.ConcurrentRegion.html>`_.
|
||||
|
||||
Timestamp region
|
||||
~~~~~~~~~~~~~~~~
|
||||
@ -120,7 +120,7 @@ Timestamp region
|
||||
|
||||
Tracks the timestamps of the most recent updates to particular entity.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.TimestampRegion.html/>`_.
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.TimestampRegion.html>`_.
|
||||
|
||||
.. _reference-second-level-cache-mode:
|
||||
|
||||
@ -209,7 +209,7 @@ It allows you to provide a specific implementation of the following components :
|
||||
* ``EntityHydrator`` Transform an entity into a cache entry and cache entry into entities
|
||||
* ``CollectionHydrator`` Transform a collection into a cache entry and cache entry into collection
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.DefaultCacheFactory.html/>`_.
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.DefaultCacheFactory.html>`_.
|
||||
|
||||
Region Lifetime
|
||||
~~~~~~~~~~~~~~~
|
||||
@ -270,7 +270,7 @@ By providing a cache logger you should be able to get information about all cach
|
||||
If you want to get more information you should implement ``\Doctrine\ORM\Cache\Logging\CacheLogger``.
|
||||
and collect all information you want.
|
||||
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.CacheLogger.html/>`_.
|
||||
`See API Doc <http://www.doctrine-project.org/api/orm/2.5/class-Doctrine.ORM.Cache.CacheLogger.html>`_.
|
||||
|
||||
|
||||
Entity cache definition
|
||||
|
@ -205,7 +205,7 @@ tables of the current model to clean up with orphaned tables.
|
||||
You can also use database introspection to update your schema
|
||||
easily with the ``updateSchema()`` method. It will compare your
|
||||
existing database schema to the passed array of
|
||||
``ClassMetdataInfo`` instances.
|
||||
``ClassMetadataInfo`` instances.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
|
@ -214,8 +214,8 @@ example we'll use an integer.
|
||||
type: entity
|
||||
fields:
|
||||
version:
|
||||
version:
|
||||
type: integer
|
||||
type: integer
|
||||
version: true
|
||||
|
||||
Alternatively a datetime type can be used (which maps to a SQL
|
||||
timestamp or datetime):
|
||||
@ -247,8 +247,8 @@ timestamp or datetime):
|
||||
type: entity
|
||||
fields:
|
||||
version:
|
||||
version:
|
||||
type: datetime
|
||||
type: datetime
|
||||
version: true
|
||||
|
||||
Version numbers (not timestamps) should however be preferred as
|
||||
they can not potentially conflict in a highly concurrent
|
||||
|
@ -716,6 +716,8 @@ methods:
|
||||
* ``in($field, array $values)``
|
||||
* ``notIn($field, array $values)``
|
||||
* ``contains($field, $value)``
|
||||
* ``startsWith($field, $value)``
|
||||
* ``endsWith($field, $value)``
|
||||
|
||||
|
||||
.. note::
|
||||
|
@ -14,7 +14,7 @@ Guide Assumptions
|
||||
-----------------
|
||||
|
||||
This guide is designed for beginners that haven't worked with Doctrine ORM
|
||||
before. There are some prerequesites for the tutorial that have to be
|
||||
before. There are some prerequisites for the tutorial that have to be
|
||||
installed:
|
||||
|
||||
- PHP (latest stable version)
|
||||
@ -118,8 +118,8 @@ Add the following directories:
|
||||
Obtaining the EntityManager
|
||||
---------------------------
|
||||
|
||||
Doctrine's public interface is the EntityManager, it provides the
|
||||
access point to the complete lifecycle management of your entities
|
||||
Doctrine's public interface is through the ``EntityManager``. This class
|
||||
provides access points to the complete lifecycle management for your entities,
|
||||
and transforms entities from and back to persistence. You have to
|
||||
configure and create it to use your entities with Doctrine 2. I
|
||||
will show the configuration steps and then discuss them step by
|
||||
@ -150,8 +150,8 @@ step:
|
||||
// obtaining the entity manager
|
||||
$entityManager = EntityManager::create($conn, $config);
|
||||
|
||||
The first require statement sets up the autoloading capabilities of Doctrine
|
||||
using the Composer autoload.
|
||||
The require_once statement sets up the class autoloading for Doctrine and
|
||||
its dependencies using Composer's autoloader.
|
||||
|
||||
The second block consists of the instantiation of the ORM
|
||||
``Configuration`` object using the Setup helper. It assumes a bunch
|
||||
@ -159,10 +159,10 @@ of defaults that you don't have to bother about for now. You can
|
||||
read up on the configuration details in the
|
||||
:doc:`reference chapter on configuration <../reference/configuration>`.
|
||||
|
||||
The third block shows the configuration options required to connect
|
||||
to a database, in my case a file-based sqlite database. All the
|
||||
The third block shows the configuration options required to connect to
|
||||
a database. In this case, we'll use a file-based SQLite database. All the
|
||||
configuration options for all the shipped drivers are given in the
|
||||
`DBAL Configuration section of the manual <http://www.doctrine-project.org/documentation/manual/2_0/en/dbal>`_.
|
||||
`DBAL Configuration section of the manual <http://docs.doctrine-project.org/projects/doctrine-dbal/en/latest/>`_.
|
||||
|
||||
The last block shows how the ``EntityManager`` is obtained from a
|
||||
factory method.
|
||||
@ -170,15 +170,10 @@ factory method.
|
||||
Generating the Database Schema
|
||||
------------------------------
|
||||
|
||||
Now that we have defined the Metadata mappings and bootstrapped the
|
||||
EntityManager we want to generate the relational database schema
|
||||
from it. Doctrine has a Command-Line Interface that allows you to
|
||||
access the SchemaTool, a component that generates the required
|
||||
tables to work with the metadata.
|
||||
|
||||
For the command-line tool to work a cli-config.php file has to be
|
||||
present in the project root directory, where you will execute the
|
||||
doctrine command. Its a fairly simple file:
|
||||
Doctrine has a command-line interface that allows you to access the SchemaTool,
|
||||
a component that can generate a relational database schema based entirely on the
|
||||
defined entity classes and their metadata. For this tool to work, a
|
||||
cli-config.php file must exist in the project root directory:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -188,40 +183,38 @@ doctrine command. Its a fairly simple file:
|
||||
|
||||
return \Doctrine\ORM\Tools\Console\ConsoleRunner::createHelperSet($entityManager);
|
||||
|
||||
You can then change into your project directory and call the
|
||||
Doctrine command-line tool:
|
||||
Change into your project directory and call the Doctrine command-line tool:
|
||||
|
||||
::
|
||||
|
||||
$ cd project/
|
||||
$ vendor/bin/doctrine orm:schema-tool:create
|
||||
|
||||
At this point no entity metadata exists in `src` so you will see a message like
|
||||
"No Metadata Classes to process." Don't worry, we'll create a Product entity and
|
||||
corresponding metadata in the next section.
|
||||
Since we haven't added any entity metadata in `src` yet, you'll see a message
|
||||
stating "No Metadata Classes to process." In the next section, we'll create a
|
||||
Product entity along with the corresponding metadata, and run this command again.
|
||||
|
||||
You should be aware that during the development process you'll periodically need
|
||||
to update your database schema to be in sync with your Entities metadata.
|
||||
|
||||
You can easily recreate the database:
|
||||
Note that as you modify your entities' metadata during the development process,
|
||||
you'll need to update your database schema to stay in sync with the metadata.
|
||||
You can rasily recreate the database using the following commands:
|
||||
|
||||
::
|
||||
|
||||
$ vendor/bin/doctrine orm:schema-tool:drop --force
|
||||
$ vendor/bin/doctrine orm:schema-tool:create
|
||||
|
||||
Or use the update functionality:
|
||||
Or you can use the update functionality:
|
||||
|
||||
::
|
||||
|
||||
$ vendor/bin/doctrine orm:schema-tool:update --force
|
||||
|
||||
The updating of databases uses a Diff Algorithm for a given
|
||||
Database Schema, a cornerstone of the ``Doctrine\DBAL`` package,
|
||||
Database Schema. This is a cornerstone of the ``Doctrine\DBAL`` package,
|
||||
which can even be used without the Doctrine ORM package.
|
||||
|
||||
Starting with the Product
|
||||
-------------------------
|
||||
Starting with the Product Entity
|
||||
--------------------------------
|
||||
|
||||
We start with the simplest entity, the Product. Create a ``src/Product.php`` file to contain the ``Product``
|
||||
entity definition:
|
||||
@ -257,9 +250,9 @@ entity definition:
|
||||
}
|
||||
}
|
||||
|
||||
Note that all fields are set to protected (not public) with a
|
||||
mutator (getter and setter) defined for every field except $id.
|
||||
The use of mutators allows Doctrine to hook into calls which
|
||||
When creating entity classes, all of the fields should be protected or private
|
||||
(not public), with getter and setter methods for each one (except $id).
|
||||
The use of mutators allows Doctrine to hook into calls which
|
||||
manipulate the entities in ways that it could not if you just
|
||||
directly set the values with ``entity#field = foo;``
|
||||
|
||||
@ -274,9 +267,10 @@ language. The metadata language describes how entities, their
|
||||
properties and references should be persisted and what constraints
|
||||
should be applied to them.
|
||||
|
||||
Metadata for entities are configured using a XML, YAML or Docblock Annotations.
|
||||
This Getting Started Guide will show the mappings for all Mapping Drivers.
|
||||
References in the text will be made to the XML mapping.
|
||||
Metadata for an Entity can be configured using DocBlock annotations directly
|
||||
in the Entity class itself, or in an external XML or YAML file. This Getting
|
||||
Started guide will demonstrate metadata mappings using all three methods,
|
||||
but you only need to choose one.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -332,27 +326,28 @@ References in the text will be made to the XML mapping.
|
||||
The top-level ``entity`` definition tag specifies information about
|
||||
the class and table-name. The primitive type ``Product#name`` is
|
||||
defined as a ``field`` attribute. The ``id`` property is defined with
|
||||
the ``id`` tag, this has a ``generator`` tag nested inside which
|
||||
defines that the primary key generation mechanism automatically
|
||||
uses the database platforms native id generation strategy (for
|
||||
example AUTO INCREMENT in the case of MySql or Sequences in the
|
||||
the ``id`` tag. It has a ``generator`` tag nested inside, which
|
||||
specifies that the primary key generation mechanism should automatically
|
||||
use the database platform's native id generation strategy (for
|
||||
example, AUTO INCREMENT in the case of MySql, or Sequences in the
|
||||
case of PostgreSql and Oracle).
|
||||
|
||||
Now that we have defined our first entity, let's update the database:
|
||||
Now that we have defined our first entity and its metadata,
|
||||
let's update the database schema:
|
||||
|
||||
::
|
||||
|
||||
$ vendor/bin/doctrine orm:schema-tool:update --force --dump-sql
|
||||
|
||||
Specifying both flags ``--force`` and ``--dump-sql`` prints and executes the DDL
|
||||
statements.
|
||||
Specifying both flags ``--force`` and ``--dump-sql`` will cause the DDL
|
||||
statements to be executed and then printed to the screen.
|
||||
|
||||
Now create a new script that will insert products into the database:
|
||||
Now, we'll create a new script to insert products into the database:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// create_product.php
|
||||
// create_product.php <name>
|
||||
require_once "bootstrap.php";
|
||||
|
||||
$newProductName = $argv[1];
|
||||
@ -372,22 +367,19 @@ Call this script from the command-line to see how new products are created:
|
||||
$ php create_product.php ORM
|
||||
$ php create_product.php DBAL
|
||||
|
||||
What is happening here? Using the ``Product`` is pretty standard OOP.
|
||||
What is happening here? Using the ``Product`` class is pretty standard OOP.
|
||||
The interesting bits are the use of the ``EntityManager`` service. To
|
||||
notify the EntityManager that a new entity should be inserted into the database
|
||||
you have to call ``persist()``. To initiate a transaction to actually perform
|
||||
the insertion, You have to explicitly call ``flush()`` on the ``EntityManager``.
|
||||
notify the EntityManager that a new entity should be inserted into the database,
|
||||
you have to call ``persist()``. To initiate a transaction to actually *perform*
|
||||
the insertion, you have to explicitly call ``flush()`` on the ``EntityManager``.
|
||||
|
||||
This distinction between persist and flush is allows to aggregate all writes
|
||||
(INSERT, UPDATE, DELETE) into one single transaction, which is executed when
|
||||
flush is called. Using this approach the write-performance is significantly
|
||||
better than in a scenario where updates are done for each entity in isolation.
|
||||
This distinction between persist and flush is what allows the aggregation of
|
||||
all database writes (INSERT, UPDATE, DELETE) into one single transaction, which
|
||||
is executed when ``flush()`` is called. Using this approach, the write-performance
|
||||
is significantly better than in a scenario in which writes are performed on
|
||||
each entity in isolation.
|
||||
|
||||
Doctrine follows the UnitOfWork pattern which additionally detects all entities
|
||||
that were fetched and have changed during the request. You don't have to keep track of
|
||||
entities yourself, when Doctrine already knows about them.
|
||||
|
||||
As a next step we want to fetch a list of all the Products. Let's create a
|
||||
Next, we'll fetch a list of all the Products in the database. Let's create a
|
||||
new script for this:
|
||||
|
||||
.. code-block:: php
|
||||
@ -404,10 +396,10 @@ new script for this:
|
||||
}
|
||||
|
||||
The ``EntityManager#getRepository()`` method can create a finder object (called
|
||||
a repository) for every entity. It is provided by Doctrine and contains some
|
||||
finder methods such as ``findAll()``.
|
||||
a repository) for every type of entity. It is provided by Doctrine and contains
|
||||
some finder methods like ``findAll()``.
|
||||
|
||||
Let's continue with displaying the name of a product based on its ID:
|
||||
Let's continue by creating a script to display the name of a product based on its ID:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -425,9 +417,13 @@ Let's continue with displaying the name of a product based on its ID:
|
||||
|
||||
echo sprintf("-%s\n", $product->getName());
|
||||
|
||||
Updating a product name demonstrates the functionality UnitOfWork of pattern
|
||||
discussed before. We only need to find a product entity and all changes to its
|
||||
properties are written to the database:
|
||||
Next we'll update a product's name, given its id. This simple example will
|
||||
help demonstrate Doctrine's implementation of the UnitOfWork pattern. Doctrine
|
||||
keeps track of all the entities that were retrieved from the Entity Manager,
|
||||
and can detect when any of those entities' properties have been modified.
|
||||
As a result, rather than needing to call ``persist($entity)`` for each individual
|
||||
entity whose properties were changed, a single call to ``flush()`` at the end of a
|
||||
request is sufficient to update the database for all of the modified entities.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -455,9 +451,8 @@ product name changed by calling the ``show_product.php`` script.
|
||||
Adding Bug and User Entities
|
||||
----------------------------
|
||||
|
||||
We continue with the bug tracker domain, by creating the missing classes
|
||||
``Bug`` and ``User`` and putting them into ``src/Bug.php`` and
|
||||
``src/User.php`` respectively.
|
||||
We continue with the bug tracker example by creating the ``Bug`` and ``User``
|
||||
classes. We'll store them in ``src/Bug.php`` and ``src/User.php``, respectively.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -561,14 +556,15 @@ We continue with the bug tracker domain, by creating the missing classes
|
||||
}
|
||||
}
|
||||
|
||||
All of the properties discussed so far are simple string and integer values,
|
||||
for example the id fields of the entities, their names, description, status and
|
||||
change dates. Next we will model the dynamic relationships between the entities
|
||||
by defining the references between entities.
|
||||
All of the properties we've seen so far are of simple types (integer, string,
|
||||
and datetime). But now, we'll add properties that will store objects of
|
||||
specific *entity types* in order to model the relationships between different
|
||||
entities.
|
||||
|
||||
References between objects are foreign keys in the database. You never have to
|
||||
(and never should) work with the foreign keys directly, only with the objects
|
||||
that represent the foreign key through their own identity.
|
||||
At the database level, relationships between entities are represented by foreign
|
||||
keys. But with Doctrine, you'll never have to (and never should) work with
|
||||
the foreign keys directly. You should only work with objects that represent
|
||||
foreign keys through their own identities.
|
||||
|
||||
For every foreign key you either have a Doctrine ManyToOne or OneToOne
|
||||
association. On the inverse sides of these foreign keys you can have
|
||||
@ -602,6 +598,7 @@ domain model to match the requirements:
|
||||
<?php
|
||||
// src/User.php
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
|
||||
class User
|
||||
{
|
||||
// ... (previous code)
|
||||
@ -616,12 +613,13 @@ domain model to match the requirements:
|
||||
}
|
||||
}
|
||||
|
||||
You use Doctrine's ArrayCollections in your Doctrine models, rather
|
||||
than plain PHP arrays, so that Doctrine can watch what happens with
|
||||
them and act appropriately. Note that if you dump your entities,
|
||||
you'll see a "PersistentCollection" in place of your ArrayCollection,
|
||||
which is just an
|
||||
internal Doctrine class with the same interface.
|
||||
.. note::
|
||||
|
||||
Whenever an entity is created from the database, a ``Collection``
|
||||
implementation of the type ``PersistentCollection`` will be injected into
|
||||
your entity instead of an ``ArrayCollection``. This helps Doctrine ORM
|
||||
understand the changes that have happened to the collection that are
|
||||
noteworthy for persistence.
|
||||
|
||||
.. warning::
|
||||
|
||||
@ -644,24 +642,22 @@ able to work with Doctrine 2. These assumptions are not unique to
|
||||
Doctrine 2 but are best practices in handling database relations
|
||||
and Object-Relational Mapping.
|
||||
|
||||
|
||||
- In a one-to-one relation, the entity holding the foreign key of
|
||||
the related entity on its own database table is *always* the owning
|
||||
side of the relation.
|
||||
- In a many-to-one relation, the Many-side is the owning side by
|
||||
default because it holds the foreign key. Accordingly, the One-side
|
||||
is the inverse side by default.
|
||||
- In a many-to-one relation, the One-side can only be the owning side if
|
||||
the relation is implemented as a ManyToMany with a join table, and the
|
||||
One-side is restricted to allow only UNIQUE values per database constraint.
|
||||
- In a many-to-many relation, both sides can be the owning side of
|
||||
the relation. However, in a bi-directional many-to-many relation,
|
||||
only one side is allowed to be the owning side.
|
||||
- Changes to Collections are saved or updated, when the entity on
|
||||
the *owning* side of the collection is saved or updated.
|
||||
- Saving an Entity at the inverse side of a relation never
|
||||
triggers a persist operation to changes to the collection.
|
||||
- In a one-to-one relation the entity holding the foreign key of
|
||||
the related entity on its own database table is *always* the owning
|
||||
side of the relation.
|
||||
- In a many-to-many relation, both sides can be the owning side of
|
||||
the relation. However in a bi-directional many-to-many relation
|
||||
only one is allowed to be.
|
||||
- In a many-to-one relation the Many-side is the owning side by
|
||||
default, because it holds the foreign key.
|
||||
- The OneToMany side of a relation is inverse by default, since
|
||||
the foreign key is saved on the Many side. A OneToMany relation can
|
||||
only be the owning side, if its implemented using a ManyToMany
|
||||
relation with join table and restricting the one side to allow only
|
||||
UNIQUE values per database constraint.
|
||||
|
||||
.. note::
|
||||
|
||||
@ -686,13 +682,13 @@ the bi-directional reference:
|
||||
protected $engineer;
|
||||
protected $reporter;
|
||||
|
||||
public function setEngineer($engineer)
|
||||
public function setEngineer(User $engineer)
|
||||
{
|
||||
$engineer->assignedToBug($this);
|
||||
$this->engineer = $engineer;
|
||||
}
|
||||
|
||||
public function setReporter($reporter)
|
||||
public function setReporter(User $reporter)
|
||||
{
|
||||
$reporter->addReportedBug($this);
|
||||
$this->reporter = $reporter;
|
||||
@ -717,15 +713,15 @@ the bi-directional reference:
|
||||
{
|
||||
// ... (previous code)
|
||||
|
||||
protected $reportedBugs = null;
|
||||
protected $assignedBugs = null;
|
||||
protected $reportedBugs;
|
||||
protected $assignedBugs;
|
||||
|
||||
public function addReportedBug($bug)
|
||||
public function addReportedBug(Bug $bug)
|
||||
{
|
||||
$this->reportedBugs[] = $bug;
|
||||
}
|
||||
|
||||
public function assignedToBug($bug)
|
||||
public function assignedToBug(Bug $bug)
|
||||
{
|
||||
$this->assignedBugs[] = $bug;
|
||||
}
|
||||
@ -741,7 +737,7 @@ You can see from ``User#addReportedBug()`` and
|
||||
``User#assignedToBug()`` that using this method in userland alone
|
||||
would not add the Bug to the collection of the owning side in
|
||||
``Bug#reporter`` or ``Bug#engineer``. Using these methods and
|
||||
calling Doctrine for persistence would not update the collections
|
||||
calling Doctrine for persistence would not update the Collections'
|
||||
representation in the database.
|
||||
|
||||
Only using ``Bug#setEngineer()`` or ``Bug#setReporter()``
|
||||
@ -749,7 +745,7 @@ correctly saves the relation information.
|
||||
|
||||
The ``Bug#reporter`` and ``Bug#engineer`` properties are
|
||||
Many-To-One relations, which point to a User. In a normalized
|
||||
relational model the foreign key is saved on the Bug's table, hence
|
||||
relational model, the foreign key is saved on the Bug's table, hence
|
||||
in our object-relation model the Bug is at the owning side of the
|
||||
relation. You should always make sure that the use-cases of your
|
||||
domain model should drive which side is an inverse or owning one in
|
||||
@ -758,7 +754,7 @@ or an engineer is assigned to the bug, we don't want to update the
|
||||
User to persist the reference, but the Bug. This is the case with
|
||||
the Bug being at the owning side of the relation.
|
||||
|
||||
Bugs reference Products by an uni-directional ManyToMany relation in
|
||||
Bugs reference Products by a uni-directional ManyToMany relation in
|
||||
the database that points from Bugs to Products.
|
||||
|
||||
.. code-block:: php
|
||||
@ -771,7 +767,7 @@ the database that points from Bugs to Products.
|
||||
|
||||
protected $products = null;
|
||||
|
||||
public function assignToProduct($product)
|
||||
public function assignToProduct(Product $product)
|
||||
{
|
||||
$this->products[] = $product;
|
||||
}
|
||||
@ -783,7 +779,7 @@ the database that points from Bugs to Products.
|
||||
}
|
||||
|
||||
We are now finished with the domain model given the requirements.
|
||||
Lets add metadata mappings for the ``User`` and ``Bug`` as we did for
|
||||
Lets add metadata mappings for the ``Bug`` entity, as we did for
|
||||
the ``Product`` before:
|
||||
|
||||
.. configuration-block::
|
||||
@ -890,13 +886,13 @@ For the "created" field we have used the ``datetime`` type,
|
||||
which translates the YYYY-mm-dd HH:mm:ss database format
|
||||
into a PHP DateTime instance and back.
|
||||
|
||||
After the field definitions the two qualified references to the
|
||||
After the field definitions, the two qualified references to the
|
||||
user entity are defined. They are created by the ``many-to-one``
|
||||
tag. The class name of the related entity has to be specified with
|
||||
the ``target-entity`` attribute, which is enough information for
|
||||
the database mapper to access the foreign-table. Since
|
||||
``reporter`` and ``engineer`` are on the owning side of a
|
||||
bi-directional relation we also have to specify the ``inversed-by``
|
||||
bi-directional relation, we also have to specify the ``inversed-by``
|
||||
attribute. They have to point to the field names on the inverse
|
||||
side of the relationship. We will see in the next example that the ``inversed-by``
|
||||
attribute has a counterpart ``mapped-by`` which makes that
|
||||
@ -907,7 +903,7 @@ holds all products where the specific bug occurs. Again
|
||||
you have to define the ``target-entity`` and ``field`` attributes
|
||||
on the ``many-to-many`` tag.
|
||||
|
||||
The last missing definition is that of the User entity:
|
||||
Finally, we'll add metadata mappings for the ``User`` entity.
|
||||
|
||||
.. configuration-block::
|
||||
|
||||
@ -934,13 +930,13 @@ The last missing definition is that of the User entity:
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Bug", mappedBy="reporter")
|
||||
* @var Bug[]
|
||||
* @var Bug[] An ArrayCollection of Bug objects.
|
||||
**/
|
||||
protected $reportedBugs = null;
|
||||
|
||||
/**
|
||||
* @OneToMany(targetEntity="Bug", mappedBy="engineer")
|
||||
* @var Bug[]
|
||||
* @var Bug[] An ArrayCollection of Bug objects.
|
||||
**/
|
||||
protected $assignedBugs = null;
|
||||
|
||||
@ -996,10 +992,7 @@ means the join details have already been defined on the owning
|
||||
side. Therefore we only have to specify the property on the Bug
|
||||
class that holds the owning sides.
|
||||
|
||||
This example has a fair overview of the most basic features of the
|
||||
metadata definition language.
|
||||
|
||||
Update your database running:
|
||||
Update your database schema by running:
|
||||
::
|
||||
|
||||
$ vendor/bin/doctrine orm:schema-tool:update --force
|
||||
@ -1008,7 +1001,8 @@ Update your database running:
|
||||
Implementing more Requirements
|
||||
------------------------------
|
||||
|
||||
For starters we need to create user entities:
|
||||
So far, we've seen the most basic features of the metadata definition language.
|
||||
To explore additional functionality, let's first create new ``User`` entities:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
@ -1032,23 +1026,22 @@ Now call:
|
||||
|
||||
$ php create_user.php beberlei
|
||||
|
||||
We now have the data to create a bug and the code for this scenario may look
|
||||
like this:
|
||||
We now have the necessary data to create a new Bug entity:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// create_bug.php
|
||||
// create_bug.php <reporter-id> <engineer-id> <product-ids>
|
||||
require_once "bootstrap.php";
|
||||
|
||||
$theReporterId = $argv[1];
|
||||
$theDefaultEngineerId = $argv[2];
|
||||
$reporterId = $argv[1];
|
||||
$engineerId = $argv[2];
|
||||
$productIds = explode(",", $argv[3]);
|
||||
|
||||
$reporter = $entityManager->find("User", $theReporterId);
|
||||
$engineer = $entityManager->find("User", $theDefaultEngineerId);
|
||||
$reporter = $entityManager->find("User", $reporterId);
|
||||
$engineer = $entityManager->find("User", $engineerId);
|
||||
if (!$reporter || !$engineer) {
|
||||
echo "No reporter and/or engineer found for the input.\n";
|
||||
echo "No reporter and/or engineer found for the given id(s).\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@ -1070,22 +1063,17 @@ like this:
|
||||
|
||||
echo "Your new Bug Id: ".$bug->getId()."\n";
|
||||
|
||||
Since we only have one user and product, probably with the ID of 1, we can call this script with:
|
||||
Since we only have one user and product, probably with the ID of 1, we can
|
||||
call this script as follows:
|
||||
|
||||
::
|
||||
|
||||
php create_bug.php 1 1 1
|
||||
|
||||
This is the first contact with the read API of the EntityManager,
|
||||
showing that a call to ``EntityManager#find($name, $id)`` returns a
|
||||
single instance of an entity queried by primary key. Besides this
|
||||
we see the persist + flush pattern again to save the Bug into the
|
||||
database.
|
||||
|
||||
See how simple relating Bug, Reporter, Engineer and Products is
|
||||
done by using the discussed methods in the "A first prototype"
|
||||
section. The UnitOfWork will detect this relationship when flush is
|
||||
called and relate them in the database appropriately.
|
||||
See how simple it is to relate a Bug, Reporter, Engineer and Products?
|
||||
Also recall that thanks to the UnitOfWork pattern, Doctrine will detect
|
||||
these relations and update all of the modified entities in the database
|
||||
automatically when ``flush()`` is called.
|
||||
|
||||
Queries for Application Use-Cases
|
||||
---------------------------------
|
||||
@ -1094,7 +1082,7 @@ List of Bugs
|
||||
~~~~~~~~~~~~
|
||||
|
||||
Using the previous examples we can fill up the database quite a
|
||||
bit, however we now need to discuss how to query the underlying
|
||||
bit. However, we now need to discuss how to query the underlying
|
||||
mapper for the required view representations. When opening the
|
||||
application, bugs can be paginated through a list-view, which is
|
||||
the first read-only use-case:
|
||||
@ -1225,7 +1213,7 @@ write scenarios:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// show_bug.php
|
||||
// show_bug.php <id>
|
||||
require_once "bootstrap.php";
|
||||
|
||||
$theBugId = $argv[1];
|
||||
@ -1300,7 +1288,7 @@ and usage of bound parameters:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// dashboard.php
|
||||
// dashboard.php <user-id>
|
||||
require_once "bootstrap.php";
|
||||
|
||||
$theUserId = $argv[1];
|
||||
@ -1366,7 +1354,7 @@ should be able to close a bug. This looks like:
|
||||
.. code-block:: php
|
||||
|
||||
<?php
|
||||
// close_bug.php
|
||||
// close_bug.php <bug-id>
|
||||
require_once "bootstrap.php";
|
||||
|
||||
$theBugId = $argv[1];
|
||||
|
@ -167,7 +167,7 @@
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="entity">
|
||||
<xs:sequence>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="cache" type="orm:cache" minOccurs="0" maxOccurs="1"/>
|
||||
<xs:element name="options" type="orm:options" minOccurs="0" />
|
||||
<xs:element name="indexes" type="orm:indexes" minOccurs="0"/>
|
||||
@ -189,9 +189,9 @@
|
||||
<xs:element name="association-overrides" type="orm:association-overrides" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:element name="attribute-overrides" type="orm:attribute-overrides" minOccurs="0" maxOccurs="unbounded" />
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
</xs:choice>
|
||||
<xs:attribute name="name" type="xs:string" use="required" />
|
||||
<xs:attribute name="table" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="table" type="orm:tablename" />
|
||||
<xs:attribute name="schema" type="xs:NMTOKEN" />
|
||||
<xs:attribute name="repository-class" type="xs:string"/>
|
||||
<xs:attribute name="inheritance-type" type="orm:inheritance-type"/>
|
||||
@ -200,6 +200,13 @@
|
||||
<xs:anyAttribute namespace="##other"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="tablename" id="tablename">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:pattern value="[a-zA-Z_u01-uff.]+" id="tablename.pattern">
|
||||
</xs:pattern>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="option" mixed="true">
|
||||
<xs:sequence minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="option" type="orm:option"/>
|
||||
@ -412,9 +419,16 @@
|
||||
<xs:sequence>
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="class" type="xs:string" use="required" />
|
||||
<xs:attribute name="class" type="orm:fqcn" use="required" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="fqcn" id="fqcn">
|
||||
<xs:restriction base="xs:token">
|
||||
<xs:pattern value="[a-zA-Z_u01-uff][a-zA-Z0-9_u01-uff]+" id="fqcn.pattern">
|
||||
</xs:pattern>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
|
||||
<xs:complexType name="inverse-join-columns">
|
||||
<xs:sequence>
|
||||
<xs:element name="join-column" type="orm:join-column" minOccurs="1" maxOccurs="unbounded" />
|
||||
@ -567,6 +581,7 @@
|
||||
<xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:NMTOKEN" use="required" />
|
||||
<xs:attribute name="fetch" type="orm:fetch-type" use="optional" />
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="inversed-by-override">
|
||||
|
@ -27,9 +27,6 @@ use Doctrine\ORM\Query\Parameter;
|
||||
use Doctrine\ORM\Cache\QueryCacheKey;
|
||||
use Doctrine\DBAL\Cache\QueryCacheProfile;
|
||||
|
||||
use Doctrine\ORM\Cache;
|
||||
use Doctrine\ORM\Query\ResultSetMapping;
|
||||
|
||||
/**
|
||||
* Base contract for ORM queries. Base class for Query and NativeQuery.
|
||||
*
|
||||
|
@ -30,7 +30,6 @@ use Doctrine\ORM\Cache\Persister\Collection\ReadWriteCachedCollectionPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\NonStrictReadWriteCachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\ReadOnlyCachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Persister\Entity\ReadWriteCachedEntityPersister;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use Doctrine\ORM\Cache\Region\DefaultMultiGetRegion;
|
||||
use Doctrine\ORM\Cache\Region\DefaultRegion;
|
||||
use Doctrine\ORM\Cache\Region\FileLockRegion;
|
||||
|
@ -122,11 +122,9 @@ class DefaultQueryCache implements QueryCache
|
||||
|
||||
// @TODO - move to cache hydration component
|
||||
foreach ($entry->result as $index => $entry) {
|
||||
|
||||
$entityEntry = is_array($entries) && array_key_exists($index, $entries) ? $entries[$index] : null;
|
||||
|
||||
if ($entityEntry === null) {
|
||||
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheMiss($regionName, $cacheKeys->identifiers[$index]);
|
||||
}
|
||||
@ -139,7 +137,6 @@ class DefaultQueryCache implements QueryCache
|
||||
}
|
||||
|
||||
if ( ! $hasRelation) {
|
||||
|
||||
$result[$index] = $this->uow->createEntity($entityEntry->class, $entityEntry->resolveAssociationEntries($this->em), self::$hints);
|
||||
|
||||
continue;
|
||||
@ -178,14 +175,20 @@ class DefaultQueryCache implements QueryCache
|
||||
continue;
|
||||
}
|
||||
|
||||
$collection = new PersistentCollection($this->em, $assocMetadata, new ArrayCollection());
|
||||
$generateKeys = function ($id) use ($assocMetadata): EntityCacheKey {
|
||||
return new EntityCacheKey($assocMetadata->rootEntityName, $id);
|
||||
};
|
||||
|
||||
$collection = new PersistentCollection($this->em, $assocMetadata, new ArrayCollection());
|
||||
$assocKeys = new CollectionCacheEntry(array_map($generateKeys, $assoc['list']));
|
||||
$assocEntries = $assocRegion->getMultiple($assocKeys);
|
||||
|
||||
foreach ($assoc['list'] as $assocIndex => $assocId) {
|
||||
$assocEntry = is_array($assocEntries) && array_key_exists($assocIndex, $assocEntries) ? $assocEntries[$assocIndex] : null;
|
||||
|
||||
if (($assocEntry = $assocRegion->get($assocKey = new EntityCacheKey($assocMetadata->rootEntityName, $assocId))) === null) {
|
||||
|
||||
if ($assocEntry === null) {
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKey);
|
||||
$this->cacheLogger->entityCacheMiss($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]);
|
||||
}
|
||||
|
||||
$this->uow->hydrationComplete();
|
||||
@ -198,7 +201,7 @@ class DefaultQueryCache implements QueryCache
|
||||
$collection->hydrateSet($assocIndex, $element);
|
||||
|
||||
if ($this->cacheLogger !== null) {
|
||||
$this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKey);
|
||||
$this->cacheLogger->entityCacheHit($assocRegion->getName(), $assocKeys->identifiers[$assocIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@
|
||||
namespace Doctrine\ORM\Cache\Region;
|
||||
|
||||
use Doctrine\Common\Cache\MultiGetCache;
|
||||
use Doctrine\ORM\Cache\Region;
|
||||
use Doctrine\ORM\Cache\CollectionCacheEntry;
|
||||
|
||||
/**
|
||||
|
@ -420,15 +420,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* @param string|callable $className Class name or a callable that returns the function.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws ORMException
|
||||
*/
|
||||
public function addCustomStringFunction($name, $className)
|
||||
{
|
||||
if (Query\Parser::isInternalFunction($name)) {
|
||||
throw ORMException::overwriteInternalDQLFunctionNotAllowed($name);
|
||||
}
|
||||
|
||||
$this->_attributes['customStringFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
@ -478,15 +472,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* @param string|callable $className Class name or a callable that returns the function.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws ORMException
|
||||
*/
|
||||
public function addCustomNumericFunction($name, $className)
|
||||
{
|
||||
if (Query\Parser::isInternalFunction($name)) {
|
||||
throw ORMException::overwriteInternalDQLFunctionNotAllowed($name);
|
||||
}
|
||||
|
||||
$this->_attributes['customNumericFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
@ -536,15 +524,9 @@ class Configuration extends \Doctrine\DBAL\Configuration
|
||||
* @param string|callable $className Class name or a callable that returns the function.
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws ORMException
|
||||
*/
|
||||
public function addCustomDatetimeFunction($name, $className)
|
||||
{
|
||||
if (Query\Parser::isInternalFunction($name)) {
|
||||
throw ORMException::overwriteInternalDQLFunctionNotAllowed($name);
|
||||
}
|
||||
|
||||
$this->_attributes['customDatetimeFunctions'][strtolower($name)] = $className;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Exception;
|
||||
use Doctrine\Common\EventManager;
|
||||
use Doctrine\DBAL\Connection;
|
||||
|
@ -76,7 +76,8 @@ class SequenceGenerator extends AbstractIdGenerator implements Serializable
|
||||
$conn = $em->getConnection();
|
||||
$sql = $conn->getDatabasePlatform()->getSequenceNextValSQL($this->_sequenceName);
|
||||
|
||||
$this->_nextValue = (int) $conn->fetchColumn($sql);
|
||||
// Using `query` to force usage of the master server in MasterSlaveConnection
|
||||
$this->_nextValue = (int) $conn->query($sql)->fetchColumn();
|
||||
$this->_maxValue = $this->_nextValue + $this->_allocationSize;
|
||||
}
|
||||
|
||||
|
@ -211,6 +211,11 @@ abstract class AbstractHydrator
|
||||
$this->_rsm = null;
|
||||
$this->_cache = [];
|
||||
$this->_metadataCache = [];
|
||||
|
||||
$this
|
||||
->_em
|
||||
->getEventManager()
|
||||
->removeEventListener([Events::onClear], $this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -358,8 +358,8 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
// Get a reference to the parent object to which the joined element belongs.
|
||||
if ($this->_rsm->isMixed && isset($this->rootAliases[$parentAlias])) {
|
||||
$first = reset($this->resultPointers);
|
||||
$parentObject = $first[key($first)];
|
||||
$objectClass = $this->resultPointers[$parentAlias];
|
||||
$parentObject = $objectClass[key($objectClass)];
|
||||
} else if (isset($this->resultPointers[$parentAlias])) {
|
||||
$parentObject = $this->resultPointers[$parentAlias];
|
||||
} else {
|
||||
@ -433,7 +433,7 @@ class ObjectHydrator extends AbstractHydrator
|
||||
|
||||
if ( ! $reflFieldValue || isset($this->_hints[Query::HINT_REFRESH]) || ($reflFieldValue instanceof Proxy && !$reflFieldValue->__isInitialized__)) {
|
||||
// we only need to take action if this value is null,
|
||||
// we refresh the entity or its an unitialized proxy.
|
||||
// we refresh the entity or its an uninitialized proxy.
|
||||
if (isset($nonemptyComponents[$dqlAlias])) {
|
||||
$element = $this->getEntity($data, $dqlAlias);
|
||||
$reflField->setValue($parentObject, $element);
|
||||
|
@ -57,4 +57,13 @@ final class AssociationOverride implements Annotation
|
||||
* @var string
|
||||
*/
|
||||
public $inversedBy;
|
||||
|
||||
/**
|
||||
* The fetching strategy to use for the association.
|
||||
*
|
||||
* @var string
|
||||
*
|
||||
* @Enum({"LAZY", "EAGER", "EXTRA_LAZY"})
|
||||
*/
|
||||
public $fetch;
|
||||
}
|
||||
|
@ -1376,7 +1376,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @param array $mapping The field mapping to validate & complete.
|
||||
*
|
||||
* @return array The validated and completed field mapping.
|
||||
* @return void
|
||||
*
|
||||
* @throws MappingException
|
||||
*/
|
||||
@ -1547,11 +1547,11 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
}
|
||||
|
||||
$mapping['cascade'] = $cascades;
|
||||
$mapping['isCascadeRemove'] = in_array('remove', $cascades);
|
||||
$mapping['isCascadeRemove'] = in_array('remove', $cascades);
|
||||
$mapping['isCascadePersist'] = in_array('persist', $cascades);
|
||||
$mapping['isCascadeRefresh'] = in_array('refresh', $cascades);
|
||||
$mapping['isCascadeMerge'] = in_array('merge', $cascades);
|
||||
$mapping['isCascadeDetach'] = in_array('detach', $cascades);
|
||||
$mapping['isCascadeMerge'] = in_array('merge', $cascades);
|
||||
$mapping['isCascadeDetach'] = in_array('detach', $cascades);
|
||||
|
||||
return $mapping;
|
||||
}
|
||||
@ -1796,7 +1796,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws MappingException If the class has a composite primary key.
|
||||
* @throws MappingException If the class doesn't have an identifier or it has a composite primary key.
|
||||
*/
|
||||
public function getSingleIdentifierFieldName()
|
||||
{
|
||||
@ -1804,6 +1804,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
throw MappingException::singleIdNotAllowedOnCompositePrimaryKey($this->name);
|
||||
}
|
||||
|
||||
if ( ! isset($this->identifier[0])) {
|
||||
throw MappingException::noIdDefined($this->name);
|
||||
}
|
||||
|
||||
return $this->identifier[0];
|
||||
}
|
||||
|
||||
@ -1813,7 +1817,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws MappingException If the class has a composite primary key.
|
||||
* @throws MappingException If the class doesn't have an identifier or it has a composite primary key.
|
||||
*/
|
||||
public function getSingleIdentifierColumnName()
|
||||
{
|
||||
@ -1848,7 +1852,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*/
|
||||
public function hasField($fieldName)
|
||||
{
|
||||
return isset($this->fieldMappings[$fieldName]);
|
||||
return isset($this->fieldMappings[$fieldName]) || isset($this->embeddedClasses[$fieldName]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2030,7 +2034,7 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
*
|
||||
* @return \Doctrine\DBAL\Types\Type|string|null
|
||||
*
|
||||
* @deprecated 3.0 remove this. this method is bogous and unreliable, since it cannot resolve the type of a column
|
||||
* @deprecated 3.0 remove this. this method is bogus and unreliable, since it cannot resolve the type of a column
|
||||
* that is derived by a referenced field on a different entity.
|
||||
*/
|
||||
public function getTypeOfColumn($columnName)
|
||||
@ -2149,6 +2153,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$mapping['joinTable'] = $overrideMapping['joinTable'];
|
||||
}
|
||||
|
||||
if (isset($overrideMapping['fetch'])) {
|
||||
$mapping['fetch'] = $overrideMapping['fetch'];
|
||||
}
|
||||
|
||||
$mapping['joinColumnFieldNames'] = null;
|
||||
$mapping['joinTableColumns'] = null;
|
||||
$mapping['sourceToTargetKeyColumns'] = null;
|
||||
@ -2299,6 +2307,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->table['name'] = $table['name'];
|
||||
}
|
||||
|
||||
if (isset($table['quoted'])) {
|
||||
$this->table['quoted'] = $table['quoted'];
|
||||
}
|
||||
|
||||
if (isset($table['schema'])) {
|
||||
$this->table['schema'] = $table['schema'];
|
||||
}
|
||||
|
@ -470,6 +470,11 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
$override['inversedBy'] = $associationOverride->inversedBy;
|
||||
}
|
||||
|
||||
// Check for `fetch`
|
||||
if ($associationOverride->fetch) {
|
||||
$override['fetch'] = constant(Mapping\ClassMetadata::class . '::FETCH_' . $associationOverride->fetch);
|
||||
}
|
||||
|
||||
$metadata->setAssociationOverride($fieldName, $override);
|
||||
}
|
||||
}
|
||||
|
@ -393,7 +393,7 @@ class DatabaseDriver implements MappingDriver
|
||||
'fieldName' => $this->getFieldNameForColumn($tableName, $column->getName(), false),
|
||||
'columnName' => $column->getName(),
|
||||
'type' => $column->getType()->getName(),
|
||||
'nullable' => ( ! $column->getNotNull()),
|
||||
'nullable' => ( ! $column->getNotnull()),
|
||||
];
|
||||
|
||||
// Type specific elements
|
||||
@ -482,7 +482,7 @@ class DatabaseDriver implements MappingDriver
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreive schema table definition foreign keys.
|
||||
* Retrieve schema table definition foreign keys.
|
||||
*
|
||||
* @param \Doctrine\DBAL\Schema\Table $table
|
||||
*
|
||||
|
@ -24,6 +24,7 @@ use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
|
||||
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
|
||||
|
||||
/**
|
||||
* XmlDriver is a metadata driver that enables mapping through XML files.
|
||||
@ -165,7 +166,7 @@ class XmlDriver extends FileDriver
|
||||
$inheritanceType = (string) $xmlRoot['inheritance-type'];
|
||||
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
|
||||
|
||||
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
if ($metadata->inheritanceType != Metadata::INHERITANCE_TYPE_NONE) {
|
||||
// Evaluate <discriminator-column...>
|
||||
if (isset($xmlRoot->{'discriminator-column'})) {
|
||||
$discrColumn = $xmlRoot->{'discriminator-column'};
|
||||
@ -621,6 +622,11 @@ class XmlDriver extends FileDriver
|
||||
$override['inversedBy'] = (string) $overrideElement->{'inversed-by'}['name'];
|
||||
}
|
||||
|
||||
// Check for `fetch`
|
||||
if (isset($overrideElement['fetch'])) {
|
||||
$override['fetch'] = constant(Metadata::class . '::FETCH_' . (string) $overrideElement['fetch']);
|
||||
}
|
||||
|
||||
$metadata->setAssociationOverride($fieldName, $override);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ namespace Doctrine\ORM\Mapping\Driver;
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\Builder\EntityListenerBuilder;
|
||||
use Doctrine\Common\Persistence\Mapping\Driver\FileDriver;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata as Metadata;
|
||||
use Doctrine\ORM\Mapping\MappingException;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
@ -174,7 +175,7 @@ class YamlDriver extends FileDriver
|
||||
if (isset($element['inheritanceType'])) {
|
||||
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType'])));
|
||||
|
||||
if ($metadata->inheritanceType != \Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_NONE) {
|
||||
if ($metadata->inheritanceType != Metadata::INHERITANCE_TYPE_NONE) {
|
||||
// Evaluate discriminatorColumn
|
||||
if (isset($element['discriminatorColumn'])) {
|
||||
$discrColumn = $element['discriminatorColumn'];
|
||||
@ -622,6 +623,11 @@ class YamlDriver extends FileDriver
|
||||
$override['inversedBy'] = (string) $associationOverrideElement['inversedBy'];
|
||||
}
|
||||
|
||||
// Check for `fetch`
|
||||
if (isset($associationOverrideElement['fetch'])) {
|
||||
$override['fetch'] = constant(Metadata::class . '::FETCH_' . $associationOverrideElement['fetch']);
|
||||
}
|
||||
|
||||
$metadata->setAssociationOverride($fieldName, $override);
|
||||
}
|
||||
}
|
||||
@ -800,6 +806,10 @@ class YamlDriver extends FileDriver
|
||||
*/
|
||||
protected function loadMappingFile($file)
|
||||
{
|
||||
if (defined(Yaml::class . '::PARSE_KEYS_AS_STRINGS')) {
|
||||
return Yaml::parse(file_get_contents($file), Yaml::PARSE_KEYS_AS_STRINGS);
|
||||
}
|
||||
|
||||
return Yaml::parse(file_get_contents($file));
|
||||
}
|
||||
}
|
||||
|
@ -424,6 +424,16 @@ class MappingException extends \Doctrine\ORM\ORMException
|
||||
return new self('Single id is not allowed on composite primary key in entity '.$entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entity
|
||||
*
|
||||
* @return MappingException
|
||||
*/
|
||||
public static function noIdDefined($entity)
|
||||
{
|
||||
return new self('No ID defined for entity ' . $entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $entity
|
||||
* @param string $fieldName
|
||||
|
@ -52,8 +52,10 @@ interface NamingStrategy
|
||||
/**
|
||||
* Returns a column name for an embedded property.
|
||||
*
|
||||
* @param string $propertyName
|
||||
* @param string $embeddedColumnName
|
||||
* @param string $propertyName
|
||||
* @param string $embeddedColumnName
|
||||
* @param string $className
|
||||
* @param string $embeddedClassName
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
@ -70,12 +72,10 @@ interface NamingStrategy
|
||||
* Returns a join column name for a property.
|
||||
*
|
||||
* @param string $propertyName A property name.
|
||||
* @param string|null $className The fully-qualified class name.
|
||||
* This parameter is omitted from the signature due to BC
|
||||
*
|
||||
* @return string A join column name.
|
||||
*/
|
||||
function joinColumnName($propertyName/*, $className = null*/);
|
||||
function joinColumnName($propertyName);
|
||||
|
||||
/**
|
||||
* Returns a join table name.
|
||||
|
@ -323,16 +323,6 @@ class ORMException extends Exception
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $functionName
|
||||
*
|
||||
* @return ORMException
|
||||
*/
|
||||
public static function overwriteInternalDQLFunctionNotAllowed($functionName)
|
||||
{
|
||||
return new self("It is not allowed to overwrite internal function '$functionName' in the DQL parser through user-defined functions.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ORMException
|
||||
*/
|
||||
|
@ -82,16 +82,18 @@ abstract class AbstractEntityInheritancePersister extends BasicEntityPersister
|
||||
/**
|
||||
* @param string $tableAlias
|
||||
* @param string $joinColumnName
|
||||
* @param string $quotedColumnName
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $type)
|
||||
protected function getSelectJoinColumnSQL($tableAlias, $joinColumnName, $quotedColumnName, $type)
|
||||
{
|
||||
$columnAlias = $this->getSQLColumnAlias($joinColumnName);
|
||||
|
||||
$this->currentPersisterContext->rsm->addMetaResult('r', $columnAlias, $joinColumnName, false, $type);
|
||||
|
||||
return $tableAlias . '.' . $joinColumnName . ' AS ' . $columnAlias;
|
||||
return $tableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;
|
||||
}
|
||||
}
|
||||
|
@ -87,16 +87,18 @@ class BasicEntityPersister implements EntityPersister
|
||||
* @var array
|
||||
*/
|
||||
static private $comparisonMap = [
|
||||
Comparison::EQ => '= %s',
|
||||
Comparison::IS => '= %s',
|
||||
Comparison::NEQ => '!= %s',
|
||||
Comparison::GT => '> %s',
|
||||
Comparison::GTE => '>= %s',
|
||||
Comparison::LT => '< %s',
|
||||
Comparison::LTE => '<= %s',
|
||||
Comparison::IN => 'IN (%s)',
|
||||
Comparison::NIN => 'NOT IN (%s)',
|
||||
Comparison::CONTAINS => 'LIKE %s',
|
||||
Comparison::EQ => '= %s',
|
||||
Comparison::IS => '= %s',
|
||||
Comparison::NEQ => '!= %s',
|
||||
Comparison::GT => '> %s',
|
||||
Comparison::GTE => '>= %s',
|
||||
Comparison::LT => '< %s',
|
||||
Comparison::LTE => '<= %s',
|
||||
Comparison::IN => 'IN (%s)',
|
||||
Comparison::NIN => 'NOT IN (%s)',
|
||||
Comparison::CONTAINS => 'LIKE %s',
|
||||
Comparison::STARTS_WITH => 'LIKE %s',
|
||||
Comparison::ENDS_WITH => 'LIKE %s',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -783,7 +785,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
|
||||
// unset the old value and set the new sql aliased value here. By definition
|
||||
// unset($identifier[$targetKeyColumn] works here with how UnitOfWork::createEntity() calls this method.
|
||||
$identifier[$this->getSQLTableAlias($targetClass->name) . "." . $targetKeyColumn] =
|
||||
$identifier[$targetClass->getFieldForColumn($targetKeyColumn)] =
|
||||
$sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity);
|
||||
|
||||
unset($identifier[$targetKeyColumn]);
|
||||
@ -1062,11 +1064,11 @@ class BasicEntityPersister implements EntityPersister
|
||||
|
||||
switch ($lockMode) {
|
||||
case LockMode::PESSIMISTIC_READ:
|
||||
$lockSql = ' ' . $this->platform->getReadLockSql();
|
||||
$lockSql = ' ' . $this->platform->getReadLockSQL();
|
||||
break;
|
||||
|
||||
case LockMode::PESSIMISTIC_WRITE:
|
||||
$lockSql = ' ' . $this->platform->getWriteLockSql();
|
||||
$lockSql = ' ' . $this->platform->getWriteLockSQL();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1327,7 +1329,7 @@ class BasicEntityPersister implements EntityPersister
|
||||
$resultColumnName = $this->getSQLColumnAlias($joinColumn['name']);
|
||||
$type = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
|
||||
|
||||
$this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $quotedColumn, $isIdentifier, $type);
|
||||
$this->currentPersisterContext->rsm->addMetaResult($alias, $resultColumnName, $joinColumn['name'], $isIdentifier, $type);
|
||||
|
||||
$columnList[] = sprintf('%s.%s AS %s', $sqlTableAlias, $quotedColumn, $resultColumnName);
|
||||
}
|
||||
@ -1518,12 +1520,12 @@ class BasicEntityPersister implements EntityPersister
|
||||
|
||||
switch ($lockMode) {
|
||||
case LockMode::PESSIMISTIC_READ:
|
||||
$lockSql = $this->platform->getReadLockSql();
|
||||
$lockSql = $this->platform->getReadLockSQL();
|
||||
|
||||
break;
|
||||
case LockMode::PESSIMISTIC_WRITE:
|
||||
|
||||
$lockSql = $this->platform->getWriteLockSql();
|
||||
$lockSql = $this->platform->getWriteLockSQL();
|
||||
break;
|
||||
}
|
||||
|
||||
@ -1868,8 +1870,9 @@ class BasicEntityPersister implements EntityPersister
|
||||
/**
|
||||
* Infers field types to be used by parameter type casting.
|
||||
*
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
* @param string $field
|
||||
* @param mixed $value
|
||||
* @param ClassMetadata $class
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
|
@ -341,13 +341,13 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
switch ($lockMode) {
|
||||
case LockMode::PESSIMISTIC_READ:
|
||||
|
||||
$lockSql = ' ' . $this->platform->getReadLockSql();
|
||||
$lockSql = ' ' . $this->platform->getReadLockSQL();
|
||||
|
||||
break;
|
||||
|
||||
case LockMode::PESSIMISTIC_WRITE:
|
||||
|
||||
$lockSql = ' ' . $this->platform->getWriteLockSql();
|
||||
$lockSql = ' ' . $this->platform->getWriteLockSQL();
|
||||
|
||||
break;
|
||||
}
|
||||
@ -462,21 +462,14 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
? $this->getSQLTableAlias($mapping['inherited'])
|
||||
: $baseTableAlias;
|
||||
|
||||
foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
$className = isset($mapping['inherited'])
|
||||
? $mapping['inherited']
|
||||
: $this->class->name;
|
||||
|
||||
$targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
|
||||
$targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
|
||||
|
||||
foreach ($mapping['joinColumns'] as $joinColumn) {
|
||||
$columnList[] = $this->getSelectJoinColumnSQL(
|
||||
$tableAlias,
|
||||
$srcColumn,
|
||||
PersisterHelper::getTypeOfColumn(
|
||||
$mapping['sourceToTargetKeyColumns'][$srcColumn],
|
||||
$targetClass,
|
||||
$this->em
|
||||
)
|
||||
$joinColumn['name'],
|
||||
$this->quoteStrategy->getJoinColumnName($joinColumn, $this->class, $this->platform),
|
||||
PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -510,21 +503,14 @@ class JoinedSubclassPersister extends AbstractEntityInheritancePersister
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($mapping['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
$className = isset($mapping['inherited'])
|
||||
? $mapping['inherited']
|
||||
: $subClass->name;
|
||||
|
||||
$targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
|
||||
$targetClass = $this->em->getClassMetadata($mapping['targetEntity']);
|
||||
|
||||
foreach ($mapping['joinColumns'] as $joinColumn) {
|
||||
$columnList[] = $this->getSelectJoinColumnSQL(
|
||||
$tableAlias,
|
||||
$srcColumn,
|
||||
PersisterHelper::getTypeOfColumn(
|
||||
$mapping['sourceToTargetKeyColumns'][$srcColumn],
|
||||
$targetClass,
|
||||
$this->em
|
||||
)
|
||||
$joinColumn['name'],
|
||||
$this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform),
|
||||
PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -89,15 +89,12 @@ class SingleTablePersister extends AbstractEntityInheritancePersister
|
||||
|
||||
$targetClass = $this->em->getClassMetadata($assoc['targetEntity']);
|
||||
|
||||
foreach ($assoc['targetToSourceKeyColumns'] as $srcColumn) {
|
||||
foreach ($assoc['joinColumns'] as $joinColumn) {
|
||||
$columnList[] = $this->getSelectJoinColumnSQL(
|
||||
$tableAlias,
|
||||
$srcColumn,
|
||||
PersisterHelper::getTypeOfColumn(
|
||||
$assoc['sourceToTargetKeyColumns'][$srcColumn],
|
||||
$targetClass,
|
||||
$this->em
|
||||
)
|
||||
$joinColumn['name'],
|
||||
$this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform),
|
||||
PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,9 @@ use Doctrine\ORM\ORMException;
|
||||
class PersisterException extends ORMException
|
||||
{
|
||||
/**
|
||||
* @param string $class
|
||||
* @param string $associationName
|
||||
*
|
||||
* @return PersisterException
|
||||
*/
|
||||
static public function matchingAssocationFieldRequiresObject($class, $associationName)
|
||||
|
@ -46,7 +46,7 @@ class SqlValueVisitor extends ExpressionVisitor
|
||||
*
|
||||
* @param \Doctrine\Common\Collections\Expr\Comparison $comparison
|
||||
*
|
||||
* @return mixed
|
||||
* @return void
|
||||
*/
|
||||
public function walkComparison(Comparison $comparison)
|
||||
{
|
||||
@ -69,7 +69,7 @@ class SqlValueVisitor extends ExpressionVisitor
|
||||
*
|
||||
* @param \Doctrine\Common\Collections\Expr\CompositeExpression $expr
|
||||
*
|
||||
* @return mixed
|
||||
* @return void
|
||||
*/
|
||||
public function walkCompositeExpression(CompositeExpression $expr)
|
||||
{
|
||||
@ -111,8 +111,18 @@ class SqlValueVisitor extends ExpressionVisitor
|
||||
{
|
||||
$value = $comparison->getValue()->getValue();
|
||||
|
||||
return $comparison->getOperator() == Comparison::CONTAINS
|
||||
? "%{$value}%"
|
||||
: $value;
|
||||
switch ($comparison->getOperator()) {
|
||||
case Comparison::CONTAINS:
|
||||
return "%{$value}%";
|
||||
|
||||
case Comparison::STARTS_WITH:
|
||||
return "{$value}%";
|
||||
|
||||
case Comparison::ENDS_WITH:
|
||||
return "%{$value}";
|
||||
|
||||
default:
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
namespace Doctrine\ORM;
|
||||
|
||||
use Doctrine\DBAL\LockMode;
|
||||
use Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\ParserResult;
|
||||
use Doctrine\ORM\Query\QueryException;
|
||||
@ -202,7 +203,7 @@ final class Query extends AbstractQuery
|
||||
*/
|
||||
public function getSQL()
|
||||
{
|
||||
return $this->_parse()->getSQLExecutor()->getSQLStatements();
|
||||
return $this->_parse()->getSqlExecutor()->getSqlStatements();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,9 +323,36 @@ final class Query extends AbstractQuery
|
||||
|
||||
list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
|
||||
|
||||
$this->evictResultSetCache(
|
||||
$executor,
|
||||
$sqlParams,
|
||||
$types,
|
||||
$this->_em->getConnection()->getParams()
|
||||
);
|
||||
|
||||
return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
|
||||
}
|
||||
|
||||
private function evictResultSetCache(
|
||||
AbstractSqlExecutor $executor,
|
||||
array $sqlParams,
|
||||
array $types,
|
||||
array $connectionParams
|
||||
) {
|
||||
if (null === $this->_queryCacheProfile || ! $this->getExpireResultCache()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$cacheDriver = $this->_queryCacheProfile->getResultCacheDriver();
|
||||
$statements = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array
|
||||
|
||||
foreach ($statements as $statement) {
|
||||
$cacheKeys = $this->_queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams);
|
||||
|
||||
$cacheDriver->delete(reset($cacheKeys));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evict entity cache region
|
||||
*/
|
||||
@ -343,6 +371,25 @@ final class Query extends AbstractQuery
|
||||
$this->_em->getCache()->evictEntityRegion($className);
|
||||
}
|
||||
|
||||
private function getAllDiscriminators(ClassMetadata $classMetadata)
|
||||
{
|
||||
// FIXME: this code is copied from SqlWalker->getAllDiscriminators()
|
||||
$hierarchyClasses = $classMetadata->subClasses;
|
||||
$hierarchyClasses[] = $classMetadata->name;
|
||||
|
||||
$discriminators = [];
|
||||
foreach ($hierarchyClasses as $class) {
|
||||
$currentMetadata = $this->getEntityManager()->getClassMetadata($class);
|
||||
$currentDiscriminator = $currentMetadata->discriminatorValue;
|
||||
|
||||
if (null !== $currentDiscriminator) {
|
||||
$discriminators[$currentDiscriminator] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $discriminators;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes query parameter mappings.
|
||||
*
|
||||
@ -370,6 +417,10 @@ final class Query extends AbstractQuery
|
||||
$value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
|
||||
}
|
||||
|
||||
if (isset($rsm->discriminatorParameters[$key]) && $value instanceof ClassMetadata) {
|
||||
$value = array_keys($this->getAllDiscriminators($value));
|
||||
}
|
||||
|
||||
$value = $this->processParameterValue($value);
|
||||
$type = ($parameter->getValue() === $value)
|
||||
? $parameter->getType()
|
||||
@ -709,7 +760,7 @@ final class Query extends AbstractQuery
|
||||
->getName();
|
||||
|
||||
return md5(
|
||||
$this->getDql() . serialize($this->_hints) .
|
||||
$this->getDQL() . serialize($this->_hints) .
|
||||
'&platform=' . $platform .
|
||||
($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
|
||||
'&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
|
||||
|
@ -41,6 +41,7 @@ class AbsFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -51,6 +52,7 @@ class AbsFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
54
lib/Doctrine/ORM/Query/AST/Functions/AvgFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/AvgFunction.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||
|
||||
/**
|
||||
* "AVG" "(" ["DISTINCT"] StringPrimary ")"
|
||||
*
|
||||
* @since 2.6
|
||||
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||
*/
|
||||
final class AvgFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @var AggregateExpression
|
||||
*/
|
||||
private $aggregateExpression;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getSql(SqlWalker $sqlWalker): string
|
||||
{
|
||||
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function parse(Parser $parser): void
|
||||
{
|
||||
$this->aggregateExpression = $parser->AggregateExpression();
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ class BitAndFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -49,6 +50,7 @@ class BitAndFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -36,6 +36,7 @@ class BitOrFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -49,6 +50,7 @@ class BitOrFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -42,6 +42,7 @@ class ConcatFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -58,6 +59,7 @@ class ConcatFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
54
lib/Doctrine/ORM/Query/AST/Functions/CountFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/CountFunction.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||
|
||||
/**
|
||||
* "COUNT" "(" ["DISTINCT"] StringPrimary ")"
|
||||
*
|
||||
* @since 2.6
|
||||
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||
*/
|
||||
final class CountFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @var AggregateExpression
|
||||
*/
|
||||
private $aggregateExpression;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getSql(SqlWalker $sqlWalker): string
|
||||
{
|
||||
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function parse(Parser $parser): void
|
||||
{
|
||||
$this->aggregateExpression = $parser->AggregateExpression();
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ class CurrentDateFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -44,6 +45,7 @@ class CurrentDateFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -36,6 +36,7 @@ class CurrentTimeFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -44,6 +45,7 @@ class CurrentTimeFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -36,6 +36,7 @@ class CurrentTimestampFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -44,6 +45,7 @@ class CurrentTimestampFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -41,6 +41,7 @@ class DateAddFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -77,6 +78,7 @@ class DateAddFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
|
@ -38,6 +38,7 @@ class DateDiffFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -49,6 +50,7 @@ class DateDiffFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(Parser $parser)
|
||||
{
|
||||
|
@ -35,6 +35,7 @@ class DateSubFunction extends DateAddFunction
|
||||
{
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(SqlWalker $sqlWalker)
|
||||
{
|
||||
|
@ -38,6 +38,7 @@ class LengthFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -48,6 +49,7 @@ class LengthFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -44,6 +44,7 @@ class LocateFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -60,6 +61,7 @@ class LocateFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -38,6 +38,7 @@ class LowerFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -48,6 +49,7 @@ class LowerFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
54
lib/Doctrine/ORM/Query/AST/Functions/MaxFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/MaxFunction.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||
|
||||
/**
|
||||
* "MAX" "(" ["DISTINCT"] StringPrimary ")"
|
||||
*
|
||||
* @since 2.6
|
||||
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||
*/
|
||||
final class MaxFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @var AggregateExpression
|
||||
*/
|
||||
private $aggregateExpression;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getSql(SqlWalker $sqlWalker): string
|
||||
{
|
||||
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function parse(Parser $parser): void
|
||||
{
|
||||
$this->aggregateExpression = $parser->AggregateExpression();
|
||||
}
|
||||
}
|
54
lib/Doctrine/ORM/Query/AST/Functions/MinFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/MinFunction.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||
|
||||
/**
|
||||
* "MIN" "(" ["DISTINCT"] StringPrimary ")"
|
||||
*
|
||||
* @since 2.6
|
||||
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||
*/
|
||||
final class MinFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @var AggregateExpression
|
||||
*/
|
||||
private $aggregateExpression;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getSql(SqlWalker $sqlWalker): string
|
||||
{
|
||||
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function parse(Parser $parser): void
|
||||
{
|
||||
$this->aggregateExpression = $parser->AggregateExpression();
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@ class ModFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -57,6 +58,7 @@ class ModFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -41,6 +41,7 @@ class SizeFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
* @todo If the collection being counted is already joined, the SQL can be simpler (more efficient).
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
@ -110,6 +111,7 @@ class SizeFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -41,6 +41,7 @@ class SqrtFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -51,6 +52,7 @@ class SqrtFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -48,6 +48,7 @@ class SubstringFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -65,6 +66,7 @@ class SubstringFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
54
lib/Doctrine/ORM/Query/AST/Functions/SumFunction.php
Normal file
54
lib/Doctrine/ORM/Query/AST/Functions/SumFunction.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\SqlWalker;
|
||||
use Doctrine\ORM\Query\AST\AggregateExpression;
|
||||
|
||||
/**
|
||||
* "SUM" "(" ["DISTINCT"] StringPrimary ")"
|
||||
*
|
||||
* @since 2.6
|
||||
* @author Mathew Davies <thepixeldeveloper@icloud.com>
|
||||
*/
|
||||
final class SumFunction extends FunctionNode
|
||||
{
|
||||
/**
|
||||
* @var AggregateExpression
|
||||
*/
|
||||
private $aggregateExpression;
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getSql(SqlWalker $sqlWalker): string
|
||||
{
|
||||
return $this->aggregateExpression->dispatch($sqlWalker);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function parse(Parser $parser): void
|
||||
{
|
||||
$this->aggregateExpression = $parser->AggregateExpression();
|
||||
}
|
||||
}
|
@ -38,6 +38,7 @@ class UpperFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -48,6 +49,7 @@ class UpperFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function parse(\Doctrine\ORM\Query\Parser $parser)
|
||||
{
|
||||
|
@ -97,7 +97,7 @@ class OrderBy
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __tostring()
|
||||
public function __toString()
|
||||
{
|
||||
return $this->preSeparator . implode($this->separator, $this->parts) . $this->postSeparator;
|
||||
}
|
||||
|
@ -65,6 +65,13 @@ class Parser
|
||||
'date_diff' => Functions\DateDiffFunction::class,
|
||||
'bit_and' => Functions\BitAndFunction::class,
|
||||
'bit_or' => Functions\BitOrFunction::class,
|
||||
|
||||
// Aggregate functions
|
||||
'min' => Functions\MinFunction::class,
|
||||
'max' => Functions\MaxFunction::class,
|
||||
'avg' => Functions\AvgFunction::class,
|
||||
'sum' => Functions\SumFunction::class,
|
||||
'count' => Functions\CountFunction::class,
|
||||
];
|
||||
|
||||
/**
|
||||
@ -171,23 +178,6 @@ class Parser
|
||||
*/
|
||||
private $identVariableExpressions = [];
|
||||
|
||||
/**
|
||||
* Checks if a function is internally defined. Used to prevent overwriting
|
||||
* of built-in functions through user-defined functions.
|
||||
*
|
||||
* @param string $functionName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
static public function isInternalFunction($functionName)
|
||||
{
|
||||
$functionName = strtolower($functionName);
|
||||
|
||||
return isset(self::$_STRING_FUNCTIONS[$functionName])
|
||||
|| isset(self::$_DATETIME_FUNCTIONS[$functionName])
|
||||
|| isset(self::$_NUMERIC_FUNCTIONS[$functionName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query parser object.
|
||||
*
|
||||
@ -197,7 +187,7 @@ class Parser
|
||||
{
|
||||
$this->query = $query;
|
||||
$this->em = $query->getEntityManager();
|
||||
$this->lexer = new Lexer($query->getDql());
|
||||
$this->lexer = new Lexer($query->getDQL());
|
||||
$this->parserResult = new ParserResult();
|
||||
}
|
||||
|
||||
@ -277,7 +267,7 @@ class Parser
|
||||
}
|
||||
|
||||
if ($this->deferredPathExpressions) {
|
||||
$this->processDeferredPathExpressions($AST);
|
||||
$this->processDeferredPathExpressions();
|
||||
}
|
||||
|
||||
if ($this->deferredResultVariables) {
|
||||
@ -482,7 +472,7 @@ class Parser
|
||||
$distance = 12;
|
||||
|
||||
// Find a position of a final word to display in error string
|
||||
$dql = $this->query->getDql();
|
||||
$dql = $this->query->getDQL();
|
||||
$length = strlen($dql);
|
||||
$pos = $token['position'] + $distance;
|
||||
$pos = strpos($dql, ' ', ($length > $pos) ? $pos : $length);
|
||||
@ -749,11 +739,9 @@ class Parser
|
||||
* SingleValuedAssociationPathExpression ::= IdentificationVariable "." SingleValuedAssociationField
|
||||
* CollectionValuedPathExpression ::= IdentificationVariable "." CollectionValuedAssociationField
|
||||
*
|
||||
* @param mixed $AST
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function processDeferredPathExpressions($AST)
|
||||
private function processDeferredPathExpressions()
|
||||
{
|
||||
foreach ($this->deferredPathExpressions as $deferredItem) {
|
||||
$pathExpression = $deferredItem['expression'];
|
||||
@ -1504,7 +1492,7 @@ class Parser
|
||||
$glimpse = $this->lexer->glimpse();
|
||||
|
||||
switch (true) {
|
||||
case ($this->isFunction($peek)):
|
||||
case ($this->isFunction()):
|
||||
$expr = $this->FunctionDeclaration();
|
||||
break;
|
||||
|
||||
@ -1724,9 +1712,15 @@ class Parser
|
||||
* RangeVariableDeclaration ::= AbstractSchemaName ["AS"] AliasIdentificationVariable
|
||||
*
|
||||
* @return \Doctrine\ORM\Query\AST\RangeVariableDeclaration
|
||||
*
|
||||
* @throws QueryException
|
||||
*/
|
||||
public function RangeVariableDeclaration()
|
||||
{
|
||||
if ($this->lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS) && $this->lexer->glimpse()['type'] === Lexer::T_SELECT) {
|
||||
$this->semanticalError('Subquery is not supported here', $this->lexer->token);
|
||||
}
|
||||
|
||||
$abstractSchemaName = $this->AbstractSchemaName();
|
||||
|
||||
$this->validateAbstractSchemaName($abstractSchemaName);
|
||||
@ -1795,7 +1789,7 @@ class Parser
|
||||
* PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
|
||||
* PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
|
||||
*
|
||||
* @return array
|
||||
* @return \Doctrine\ORM\Query\AST\PartialObjectExpression
|
||||
*/
|
||||
public function PartialObjectExpression()
|
||||
{
|
||||
@ -1980,9 +1974,6 @@ class Parser
|
||||
// SUM(u.id) + COUNT(u.id)
|
||||
return $this->SimpleArithmeticExpression();
|
||||
|
||||
case ($this->isAggregateFunction($this->lexer->lookahead['type'])):
|
||||
return $this->AggregateExpression();
|
||||
|
||||
default:
|
||||
// IDENTITY(u)
|
||||
return $this->FunctionDeclaration();
|
||||
@ -2211,11 +2202,6 @@ class Parser
|
||||
$expression = $this->ScalarExpression();
|
||||
break;
|
||||
|
||||
case ($this->isAggregateFunction($lookaheadType)):
|
||||
// COUNT(u.id)
|
||||
$expression = $this->AggregateExpression();
|
||||
break;
|
||||
|
||||
default:
|
||||
// IDENTITY(u)
|
||||
$expression = $this->FunctionDeclaration();
|
||||
@ -2860,10 +2846,6 @@ class Parser
|
||||
$peek = $this->lexer->glimpse();
|
||||
|
||||
if ($peek['value'] == '(') {
|
||||
if ($this->isAggregateFunction($this->lexer->lookahead['type'])) {
|
||||
return $this->AggregateExpression();
|
||||
}
|
||||
|
||||
return $this->FunctionDeclaration();
|
||||
}
|
||||
|
||||
@ -2934,11 +2916,6 @@ class Parser
|
||||
case Lexer::T_COALESCE:
|
||||
case Lexer::T_NULLIF:
|
||||
return $this->CaseExpression();
|
||||
|
||||
default:
|
||||
if ($this->isAggregateFunction($lookaheadType)) {
|
||||
return $this->AggregateExpression();
|
||||
}
|
||||
}
|
||||
|
||||
$this->syntaxError(
|
||||
@ -3238,10 +3215,6 @@ class Parser
|
||||
$expr = $this->CoalesceExpression();
|
||||
break;
|
||||
|
||||
case $this->isAggregateFunction($this->lexer->lookahead['type']):
|
||||
$expr = $this->AggregateExpression();
|
||||
break;
|
||||
|
||||
case $this->isFunction():
|
||||
$expr = $this->FunctionDeclaration();
|
||||
break;
|
||||
@ -3378,8 +3351,13 @@ class Parser
|
||||
$token = $this->lexer->lookahead;
|
||||
$funcName = strtolower($token['value']);
|
||||
|
||||
// Check for built-in functions first!
|
||||
$customFunctionDeclaration = $this->CustomFunctionDeclaration();
|
||||
|
||||
// Check for custom functions functions first!
|
||||
switch (true) {
|
||||
case $customFunctionDeclaration !== null:
|
||||
return $customFunctionDeclaration;
|
||||
|
||||
case (isset(self::$_STRING_FUNCTIONS[$funcName])):
|
||||
return $this->FunctionsReturningStrings();
|
||||
|
||||
@ -3390,7 +3368,7 @@ class Parser
|
||||
return $this->FunctionsReturningDatetime();
|
||||
|
||||
default:
|
||||
return $this->CustomFunctionDeclaration();
|
||||
$this->syntaxError('known function', $token);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3418,7 +3396,7 @@ class Parser
|
||||
return $this->CustomFunctionsReturningDatetime();
|
||||
|
||||
default:
|
||||
$this->syntaxError('known function', $token);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
namespace Doctrine\ORM\Query;
|
||||
|
||||
use Doctrine\ORM\Query\AST\PathExpression;
|
||||
|
||||
/**
|
||||
* Description of QueryException.
|
||||
*
|
||||
@ -204,13 +206,15 @@ class QueryException extends \Doctrine\ORM\ORMException
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PathExpression $pathExpr
|
||||
*
|
||||
* @return QueryException
|
||||
*/
|
||||
public static function associationPathInverseSideNotSupported()
|
||||
public static function associationPathInverseSideNotSupported(PathExpression $pathExpr)
|
||||
{
|
||||
return new self(
|
||||
"A single-valued association path expression to an inverse side is not supported".
|
||||
" in DQL queries. Use an explicit join instead."
|
||||
'A single-valued association path expression to an inverse side is not supported in DQL queries. ' .
|
||||
'Instead of "' . $pathExpr->identificationVariable . '.' . $pathExpr->field . '" use an explicit join.'
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -185,6 +185,16 @@ class QueryExpressionVisitor extends ExpressionVisitor
|
||||
$parameter->setValue('%' . $parameter->getValue() . '%', $parameter->getType());
|
||||
$this->parameters[] = $parameter;
|
||||
|
||||
return $this->expr->like($field, $placeholder);
|
||||
case Comparison::STARTS_WITH:
|
||||
$parameter->setValue($parameter->getValue() . '%', $parameter->getType());
|
||||
$this->parameters[] = $parameter;
|
||||
|
||||
return $this->expr->like($field, $placeholder);
|
||||
case Comparison::ENDS_WITH:
|
||||
$parameter->setValue('%' . $parameter->getValue(), $parameter->getType());
|
||||
$this->parameters[] = $parameter;
|
||||
|
||||
return $this->expr->like($field, $placeholder);
|
||||
default:
|
||||
$operator = self::convertComparisonOperator($comparison->getOperator());
|
||||
|
@ -168,6 +168,13 @@ class ResultSetMapping
|
||||
*/
|
||||
public $metadataParameterMapping = [];
|
||||
|
||||
/**
|
||||
* Contains query parameter names to be resolved as discriminator values
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $discriminatorParameters = [];
|
||||
|
||||
/**
|
||||
* Adds an entity result to this ResultSetMapping.
|
||||
*
|
||||
|
@ -643,6 +643,7 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
$sql = '';
|
||||
|
||||
/* @var $pathExpr Query\AST\PathExpression */
|
||||
switch ($pathExpr->type) {
|
||||
case AST\PathExpression::TYPE_STATE_FIELD:
|
||||
$fieldName = $pathExpr->field;
|
||||
@ -670,7 +671,7 @@ class SqlWalker implements TreeWalker
|
||||
$assoc = $class->associationMappings[$fieldName];
|
||||
|
||||
if ( ! $assoc['isOwningSide']) {
|
||||
throw QueryException::associationPathInverseSideNotSupported();
|
||||
throw QueryException::associationPathInverseSideNotSupported($pathExpr);
|
||||
}
|
||||
|
||||
// COMPOSITE KEYS NOT (YET?) SUPPORTED
|
||||
@ -763,7 +764,8 @@ class SqlWalker implements TreeWalker
|
||||
$columnAlias = $this->getSQLColumnAlias($columnName);
|
||||
$columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
|
||||
|
||||
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
|
||||
$quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $class, $this->platform);
|
||||
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;
|
||||
|
||||
$this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $isIdentifier, $columnType);
|
||||
}
|
||||
@ -791,7 +793,8 @@ class SqlWalker implements TreeWalker
|
||||
$columnAlias = $this->getSQLColumnAlias($columnName);
|
||||
$columnType = PersisterHelper::getTypeOfColumn($joinColumn['referencedColumnName'], $targetClass, $this->em);
|
||||
|
||||
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias;
|
||||
$quotedColumnName = $this->quoteStrategy->getJoinColumnName($joinColumn, $subClass, $this->platform);
|
||||
$sqlSelectExpressions[] = $sqlTableAlias . '.' . $quotedColumnName . ' AS ' . $columnAlias;
|
||||
|
||||
$this->rsm->addMetaResult($dqlAlias, $columnAlias, $columnName, $subClass->isIdentifier($columnName), $columnType);
|
||||
}
|
||||
@ -1499,8 +1502,8 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AST\NewObjectExpression $newObjectExpression
|
||||
*
|
||||
* @param AST\NewObjectExpression $newObjectExpression
|
||||
* @param null|string $newObjectResultAlias
|
||||
* @return string The SQL.
|
||||
*/
|
||||
public function walkNewObject($newObjectExpression, $newObjectResultAlias=null)
|
||||
@ -1576,12 +1579,6 @@ class SqlWalker implements TreeWalker
|
||||
$sql .= $this->walkPathExpression($expr);
|
||||
break;
|
||||
|
||||
case ($expr instanceof AST\AggregateExpression):
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
|
||||
|
||||
$sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias;
|
||||
break;
|
||||
|
||||
case ($expr instanceof AST\Subselect):
|
||||
$alias = $simpleSelectExpression->fieldIdentificationVariable ?: $this->scalarResultCounter++;
|
||||
|
||||
@ -1758,7 +1755,7 @@ class SqlWalker implements TreeWalker
|
||||
public function walkWhereClause($whereClause)
|
||||
{
|
||||
$condSql = null !== $whereClause ? $this->walkConditionalExpression($whereClause->conditionalExpression) : '';
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSql($this->rootAliases);
|
||||
$discrSql = $this->_generateDiscriminatorColumnConditionSQL($this->rootAliases);
|
||||
|
||||
if ($this->em->hasFilters()) {
|
||||
$filterClauses = [];
|
||||
@ -2273,42 +2270,29 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMetadataInfo $discrClass
|
||||
* @param ClassMetadataInfo $rootClass
|
||||
* @param AST\InstanceOfExpression $instanceOfExpr
|
||||
* @return string The list in parentheses of valid child discriminators from the given class
|
||||
* @throws QueryException
|
||||
*/
|
||||
private function getChildDiscriminatorsFromClassMetadata(ClassMetadataInfo $discrClass, AST\InstanceOfExpression $instanceOfExpr)
|
||||
private function getChildDiscriminatorsFromClassMetadata(ClassMetadataInfo $rootClass, AST\InstanceOfExpression $instanceOfExpr)
|
||||
{
|
||||
$sqlParameterList = [];
|
||||
$discriminators = [];
|
||||
foreach ($instanceOfExpr->value as $parameter) {
|
||||
if ($parameter instanceof AST\InputParameter) {
|
||||
$this->rsm->addMetadataParameterMapping($parameter->name, 'discriminatorValue');
|
||||
|
||||
$sqlParameterList[] = $this->walkInputParameter($parameter);
|
||||
|
||||
$this->rsm->discriminatorParameters[$parameter->name] = $parameter->name;
|
||||
$sqlParameterList[] = $this->walkInParameter($parameter);
|
||||
continue;
|
||||
}
|
||||
|
||||
$metadata = $this->em->getClassMetadata($parameter);
|
||||
|
||||
if ($metadata->getName() !== $discrClass->name && ! $metadata->getReflectionClass()->isSubclassOf($discrClass->name)) {
|
||||
throw QueryException::instanceOfUnrelatedClass($parameter, $discrClass->name);
|
||||
if ($metadata->getName() !== $rootClass->name && ! $metadata->getReflectionClass()->isSubclassOf($rootClass->name)) {
|
||||
throw QueryException::instanceOfUnrelatedClass($parameter, $rootClass->name);
|
||||
}
|
||||
|
||||
// Include discriminators for parameter class and its subclass
|
||||
$hierarchyClasses = $metadata->subClasses;
|
||||
$hierarchyClasses[] = $metadata->name;
|
||||
|
||||
foreach ($hierarchyClasses as $class) {
|
||||
$currentMetadata = $this->em->getClassMetadata($class);
|
||||
$currentDiscriminator = $currentMetadata->discriminatorValue;
|
||||
|
||||
if (null !== $currentDiscriminator) {
|
||||
$discriminators[$currentDiscriminator] = null;
|
||||
}
|
||||
}
|
||||
$discriminators = $discriminators + $this->getAllDiscriminators($metadata);
|
||||
}
|
||||
|
||||
foreach (array_keys($discriminators) as $dis) {
|
||||
@ -2317,4 +2301,23 @@ class SqlWalker implements TreeWalker
|
||||
|
||||
return '(' . implode(', ', $sqlParameterList) . ')';
|
||||
}
|
||||
|
||||
private function getAllDiscriminators(ClassMetadata $classMetadata)
|
||||
{
|
||||
// FIXME: this code is identical to Query->getAllDiscriminators()
|
||||
$hierarchyClasses = $classMetadata->subClasses;
|
||||
$hierarchyClasses[] = $classMetadata->name;
|
||||
|
||||
$discriminators = [];
|
||||
foreach ($hierarchyClasses as $class) {
|
||||
$currentMetadata = $this->em->getClassMetadata($class);
|
||||
$currentDiscriminator = $currentMetadata->discriminatorValue;
|
||||
|
||||
if (null !== $currentDiscriminator) {
|
||||
$discriminators[$currentDiscriminator] = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $discriminators;
|
||||
}
|
||||
}
|
||||
|
@ -526,7 +526,7 @@ class QueryBuilder
|
||||
*
|
||||
* @param string|integer $key The parameter position or name.
|
||||
* @param mixed $value The parameter value.
|
||||
* @param string|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant
|
||||
* @param string|integer|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
|
@ -21,7 +21,6 @@ namespace Doctrine\ORM\Tools\Console\Command;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console;
|
||||
use Doctrine\ORM\Tools\Export\ClassMetadataExporter;
|
||||
use Doctrine\ORM\Tools\ConvertDoctrine1Schema;
|
||||
use Doctrine\ORM\Tools\EntityGenerator;
|
||||
|
@ -185,7 +185,7 @@ EOT
|
||||
|
||||
if (count($matches) > 1) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'Entity name "%s" is ambigous, possible matches: "%s"',
|
||||
'Entity name "%s" is ambiguous, possible matches: "%s"',
|
||||
$entityName, implode(', ', $matches)
|
||||
));
|
||||
}
|
||||
|
@ -84,10 +84,6 @@ class MetadataFilter extends \FilterIterator implements \Countable
|
||||
);
|
||||
}
|
||||
|
||||
if ($pregResult === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($pregResult) {
|
||||
return true;
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ class DebugUnitOfWorkListener
|
||||
if (is_object($var)) {
|
||||
$refl = new \ReflectionObject($var);
|
||||
|
||||
return $refl->getShortname();
|
||||
return $refl->getShortName();
|
||||
}
|
||||
|
||||
return gettype($var);
|
||||
@ -176,7 +176,7 @@ class DebugUnitOfWorkListener
|
||||
$idstring .= " [REMOVED]";
|
||||
} elseif ($state == UnitOfWork::STATE_MANAGED) {
|
||||
$idstring .= " [MANAGED]";
|
||||
} elseif ($state == UnitOfwork::STATE_DETACHED) {
|
||||
} elseif ($state == UnitOfWork::STATE_DETACHED) {
|
||||
$idstring .= " [DETACHED]";
|
||||
}
|
||||
|
||||
|
@ -1731,8 +1731,12 @@ public function __construct(<params>)
|
||||
|
||||
$embedded = ['class="' . $embeddedClass['class'] . '"'];
|
||||
|
||||
if (isset($fieldMapping['columnPrefix'])) {
|
||||
$embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true);
|
||||
if (isset($embeddedClass['columnPrefix'])) {
|
||||
if (is_string($embeddedClass['columnPrefix'])) {
|
||||
$embedded[] = 'columnPrefix="' . $embeddedClass['columnPrefix'] . '"';
|
||||
} else {
|
||||
$embedded[] = 'columnPrefix=' . var_export($embeddedClass['columnPrefix'], true);
|
||||
}
|
||||
}
|
||||
|
||||
$lines[] = $this->spaces . ' * @' .
|
||||
|
@ -91,6 +91,14 @@ class CountOutputWalker extends SqlWalker
|
||||
|
||||
$sql = parent::walkSelectStatement($AST);
|
||||
|
||||
if ($AST->groupByClause) {
|
||||
return sprintf(
|
||||
'SELECT %s AS dctrn_count FROM (%s) dctrn_table',
|
||||
$this->platform->getCountExpression('*'),
|
||||
$sql
|
||||
);
|
||||
}
|
||||
|
||||
// Find out the SQL alias of the identifier column of the root entity
|
||||
// It may be possible to make this work with multiple root entities but that
|
||||
// would probably require issuing multiple queries or doing a UNION SELECT
|
||||
|
@ -40,6 +40,7 @@ class RowNumberOverFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
@ -50,6 +51,7 @@ class RowNumberOverFunction extends FunctionNode
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @inheritdoc
|
||||
*
|
||||
* @throws ORMException
|
||||
*/
|
||||
|
@ -80,7 +80,7 @@ class ResolveTargetEntityListener implements EventSubscriber
|
||||
$args->setFoundMetadata(
|
||||
$args
|
||||
->getObjectManager()
|
||||
->getClassMetadata($this->resolveTargetEntities[$args->getClassname()]['targetEntity'])
|
||||
->getClassMetadata($this->resolveTargetEntities[$args->getClassName()]['targetEntity'])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -351,15 +351,14 @@ class SchemaTool
|
||||
* @param ClassMetadata $class
|
||||
* @param Table $table
|
||||
*
|
||||
* @return array The portable column definition of the discriminator column as required by
|
||||
* the DBAL.
|
||||
* @return void
|
||||
*/
|
||||
private function addDiscriminatorColumnDefinition($class, Table $table)
|
||||
{
|
||||
$discrColumn = $class->discriminatorColumn;
|
||||
|
||||
if ( ! isset($discrColumn['type']) ||
|
||||
(strtolower($discrColumn['type']) == 'string' && $discrColumn['length'] === null)
|
||||
(strtolower($discrColumn['type']) == 'string' && ! isset($discrColumn['length']))
|
||||
) {
|
||||
$discrColumn['type'] = 'string';
|
||||
$discrColumn['length'] = 255;
|
||||
@ -384,7 +383,7 @@ class SchemaTool
|
||||
* @param ClassMetadata $class
|
||||
* @param Table $table
|
||||
*
|
||||
* @return array The list of portable column definitions as required by the DBAL.
|
||||
* @return void
|
||||
*/
|
||||
private function gatherColumns($class, Table $table)
|
||||
{
|
||||
@ -401,12 +400,6 @@ class SchemaTool
|
||||
$pkColumns[] = $this->quoteStrategy->getColumnName($mapping['fieldName'], $class, $this->platform);
|
||||
}
|
||||
}
|
||||
|
||||
// For now, this is a hack required for single table inheritence, since this method is called
|
||||
// twice by single table inheritence relations
|
||||
if (!$table->hasIndex('primary')) {
|
||||
//$table->setPrimaryKey($pkColumns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -416,7 +409,7 @@ class SchemaTool
|
||||
* @param array $mapping The field mapping.
|
||||
* @param Table $table
|
||||
*
|
||||
* @return array The portable column definition as required by the DBAL.
|
||||
* @return void
|
||||
*/
|
||||
private function gatherColumn($class, array $mapping, Table $table)
|
||||
{
|
||||
|
@ -90,7 +90,7 @@ class SchemaValidator
|
||||
|
||||
foreach ($class->fieldMappings as $fieldName => $mapping) {
|
||||
if (!Type::hasType($mapping['type'])) {
|
||||
$ce[] = "The field '" . $class->name . "#" . $fieldName."' uses a non-existant type '" . $mapping['type'] . "'.";
|
||||
$ce[] = "The field '" . $class->name . "#" . $fieldName."' uses a non-existent type '" . $mapping['type'] . "'.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,14 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
class NegativeToPositiveType extends Type
|
||||
{
|
||||
const NAME = 'negative_to_positive';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'negative_to_positive';
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -7,12 +7,14 @@ use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
|
||||
class UpperCaseStringType extends StringType
|
||||
{
|
||||
const NAME = 'upper_case_string';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'upper_case_string';
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Mocks;
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Driver\Statement;
|
||||
|
||||
/**
|
||||
* Mock class for Connection.
|
||||
@ -13,6 +15,16 @@ class ConnectionMock extends Connection
|
||||
*/
|
||||
private $_fetchOneResult;
|
||||
|
||||
/**
|
||||
* @var \Exception|null
|
||||
*/
|
||||
private $_fetchOneException;
|
||||
|
||||
/**
|
||||
* @var Statement|null
|
||||
*/
|
||||
private $_queryResult;
|
||||
|
||||
/**
|
||||
* @var DatabasePlatformMock
|
||||
*/
|
||||
@ -86,9 +98,21 @@ class ConnectionMock extends Connection
|
||||
*/
|
||||
public function fetchColumn($statement, array $params = [], $colnum = 0, array $types = [])
|
||||
{
|
||||
if (null !== $this->_fetchOneException) {
|
||||
throw $this->_fetchOneException;
|
||||
}
|
||||
|
||||
return $this->_fetchOneResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function query() : Statement
|
||||
{
|
||||
return $this->_queryResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
@ -112,6 +136,16 @@ class ConnectionMock extends Connection
|
||||
$this->_fetchOneResult = $fetchOneResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Exception|null $exception
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setFetchOneException(\Exception $exception = null)
|
||||
{
|
||||
$this->_fetchOneException = $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
|
||||
*
|
||||
@ -132,6 +166,14 @@ class ConnectionMock extends Connection
|
||||
$this->_lastInsertId = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Statement $result
|
||||
*/
|
||||
public function setQueryResult(Statement $result)
|
||||
{
|
||||
$this->_queryResult = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
|
@ -30,13 +30,12 @@ class HydratorMockStatement implements \IteratorAggregate, Statement
|
||||
/**
|
||||
* Fetches all rows from the result set.
|
||||
*
|
||||
* @param int|null $fetchStyle
|
||||
* @param int|null $columnIndex
|
||||
* @param int|null $fetchMode
|
||||
* @param int|null $fetchArgument
|
||||
* @param array|null $ctorArgs
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function fetchAll($fetchStyle = null, $columnIndex = null, array $ctorArgs = null)
|
||||
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
|
||||
{
|
||||
return $this->_resultSet;
|
||||
}
|
||||
@ -55,7 +54,7 @@ class HydratorMockStatement implements \IteratorAggregate, Statement
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($fetchStyle = null)
|
||||
public function fetch($fetchStyle = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
|
||||
{
|
||||
$current = current($this->_resultSet);
|
||||
next($this->_resultSet);
|
||||
@ -108,7 +107,7 @@ class HydratorMockStatement implements \IteratorAggregate, Statement
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function execute($params = [])
|
||||
public function execute($params = null)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -34,12 +34,12 @@ class StatementArrayMock extends StatementMock
|
||||
}
|
||||
}
|
||||
|
||||
public function fetchAll($fetchStyle = null)
|
||||
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
|
||||
{
|
||||
return $this->_result;
|
||||
}
|
||||
|
||||
public function fetch($fetchStyle = null)
|
||||
public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
|
||||
{
|
||||
$current = current($this->_result);
|
||||
next($this->_result);
|
||||
|
@ -75,14 +75,14 @@ class StatementMock implements \IteratorAggregate, Statement
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetch($fetchStyle = null)
|
||||
public function fetch($fetchMode = null, $cursorOrientation = \PDO::FETCH_ORI_NEXT, $cursorOffset = 0)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function fetchAll($fetchStyle = null)
|
||||
public function fetchAll($fetchMode = null, $fetchArgument = null, $ctorArgs = null)
|
||||
{
|
||||
}
|
||||
|
||||
|
53
tests/Doctrine/Tests/Models/DDC5934/DDC5934BaseContract.php
Normal file
53
tests/Doctrine/Tests/Models/DDC5934/DDC5934BaseContract.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC5934;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Mapping\Column;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\GeneratedValue;
|
||||
use Doctrine\ORM\Mapping\Id;
|
||||
use Doctrine\ORM\Mapping\ManyToMany;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC5934BaseContract
|
||||
{
|
||||
/**
|
||||
* @Id()
|
||||
* @Column(name="id", type="integer")
|
||||
* @GeneratedValue()
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @var ArrayCollection
|
||||
*
|
||||
* @ManyToMany(targetEntity="DDC5934Member", fetch="LAZY", inversedBy="contracts")
|
||||
*/
|
||||
public $members;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->members = new ArrayCollection();
|
||||
}
|
||||
|
||||
public static function loadMetadata(ClassMetadata $metadata)
|
||||
{
|
||||
$metadata->mapField([
|
||||
'id' => true,
|
||||
'fieldName' => 'id',
|
||||
'type' => 'integer',
|
||||
'columnName' => 'id',
|
||||
]);
|
||||
|
||||
$metadata->mapManyToMany([
|
||||
'fieldName' => 'members',
|
||||
'targetEntity' => 'DDC5934Member',
|
||||
]);
|
||||
|
||||
$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_AUTO);
|
||||
}
|
||||
}
|
24
tests/Doctrine/Tests/Models/DDC5934/DDC5934Contract.php
Normal file
24
tests/Doctrine/Tests/Models/DDC5934/DDC5934Contract.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC5934;
|
||||
|
||||
use Doctrine\ORM\Mapping\AssociationOverride;
|
||||
use Doctrine\ORM\Mapping\AssociationOverrides;
|
||||
use Doctrine\ORM\Mapping\Entity;
|
||||
use Doctrine\ORM\Mapping\ClassMetadata;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @AssociationOverrides(
|
||||
* @AssociationOverride(name="members", fetch="EXTRA_LAZY")
|
||||
* )
|
||||
*/
|
||||
class DDC5934Contract extends DDC5934BaseContract
|
||||
{
|
||||
public static function loadMetadata(ClassMetadata $metadata)
|
||||
{
|
||||
$metadata->setAssociationOverride('members', [
|
||||
'fetch' => ClassMetadata::FETCH_EXTRA_LAZY,
|
||||
]);
|
||||
}
|
||||
}
|
24
tests/Doctrine/Tests/Models/DDC5934/DDC5934Member.php
Normal file
24
tests/Doctrine/Tests/Models/DDC5934/DDC5934Member.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC5934;
|
||||
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
/**
|
||||
* @ORM\Entity()
|
||||
*/
|
||||
class DDC5934Member
|
||||
{
|
||||
/**
|
||||
* @var ArrayCollection
|
||||
*
|
||||
* @ORM\ManyToMany(targetEntity="DDC5934BaseContract", mappedBy="members")
|
||||
*/
|
||||
public $contracts;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->contracts = new ArrayCollection();
|
||||
}
|
||||
}
|
21
tests/Doctrine/Tests/Models/DDC6412/DDC6412File.php
Normal file
21
tests/Doctrine/Tests/Models/DDC6412/DDC6412File.php
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\DDC6412;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class DDC6412File
|
||||
{
|
||||
/**
|
||||
* @Column(type="integer")
|
||||
* @GeneratedValue
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(length=50, name="file_name")
|
||||
*/
|
||||
public $name;
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ namespace Doctrine\Tests\Models\Quote;
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="`quote-address`")
|
||||
* @InheritanceType("SINGLE_TABLE")
|
||||
* @DiscriminatorColumn(name="type", type="string")
|
||||
* @DiscriminatorMap({"simple" = Address::class, "full" = FullAddress::class})
|
||||
*/
|
||||
class Address
|
||||
{
|
||||
@ -51,4 +54,4 @@ class Address
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
27
tests/Doctrine/Tests/Models/Quote/City.php
Normal file
27
tests/Doctrine/Tests/Models/Quote/City.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Quote;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="`quote-city`")
|
||||
*/
|
||||
class City
|
||||
{
|
||||
/**
|
||||
* @Id
|
||||
* @GeneratedValue
|
||||
* @Column(type="integer", name="`city-id`")
|
||||
*/
|
||||
public $id;
|
||||
|
||||
/**
|
||||
* @Column(name="`city-name`")
|
||||
*/
|
||||
public $name;
|
||||
|
||||
public function __construct(string $name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
17
tests/Doctrine/Tests/Models/Quote/FullAddress.php
Normal file
17
tests/Doctrine/Tests/Models/Quote/FullAddress.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\Models\Quote;
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
*/
|
||||
class FullAddress extends Address
|
||||
{
|
||||
/**
|
||||
* @OneToOne(targetEntity=City::class, cascade={"persist"})
|
||||
* @JoinColumn(name="`city-id`", referencedColumnName="`city-id`")
|
||||
*
|
||||
* @var City
|
||||
*/
|
||||
public $city;
|
||||
}
|
@ -125,8 +125,7 @@ class DefaultQueryCacheTest extends OrmTestCase
|
||||
$stateClass = $this->em->getClassMetadata(State::class);
|
||||
|
||||
$rsm->addRootEntityFromClassMetadata(City::class, 'c');
|
||||
$rsm->addJoinedEntityFromClassMetadata(State::class, 's', 'c', 'state', ['id'=>'state_id', 'name'=>'state_name']
|
||||
);
|
||||
$rsm->addJoinedEntityFromClassMetadata(State::class, 's', 'c', 'state', ['id'=>'state_id', 'name'=>'state_name']);
|
||||
|
||||
for ($i = 0; $i < 4; $i++) {
|
||||
$state = new State("State $i");
|
||||
@ -283,8 +282,47 @@ class DefaultQueryCacheTest extends OrmTestCase
|
||||
$key = new QueryCacheKey('query.key1', 0);
|
||||
$entry = new QueryCacheEntry(
|
||||
[
|
||||
['identifier' => ['id' => 1]],
|
||||
['identifier' => ['id' => 2]]
|
||||
['identifier' => ['id' => 1]],
|
||||
['identifier' => ['id' => 2]]
|
||||
]
|
||||
);
|
||||
|
||||
$data = [
|
||||
['id'=>1, 'name' => 'Foo'],
|
||||
['id'=>2, 'name' => 'Bar']
|
||||
];
|
||||
|
||||
$this->region->addReturn('get', $entry);
|
||||
|
||||
$this->region->addReturn(
|
||||
'getMultiple',
|
||||
[
|
||||
new EntityCacheEntry(Country::class, $data[0]),
|
||||
new EntityCacheEntry(Country::class, $data[1])
|
||||
]
|
||||
);
|
||||
|
||||
$rsm->addRootEntityFromClassMetadata(Country::class, 'c');
|
||||
|
||||
$result = $this->queryCache->get($key, $rsm);
|
||||
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertInstanceOf(Country::class, $result[0]);
|
||||
$this->assertInstanceOf(Country::class, $result[1]);
|
||||
$this->assertEquals(1, $result[0]->getId());
|
||||
$this->assertEquals(2, $result[1]->getId());
|
||||
$this->assertEquals('Foo', $result[0]->getName());
|
||||
$this->assertEquals('Bar', $result[1]->getName());
|
||||
}
|
||||
|
||||
public function testGetWithAssociation()
|
||||
{
|
||||
$rsm = new ResultSetMappingBuilder($this->em);
|
||||
$key = new QueryCacheKey('query.key1', 0);
|
||||
$entry = new QueryCacheEntry(
|
||||
[
|
||||
['identifier' => ['id' => 1]],
|
||||
['identifier' => ['id' => 2]]
|
||||
]
|
||||
);
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
namespace Doctrine\Tests\ORM\Cache;
|
||||
|
||||
use Doctrine\Common\Cache\ApcCache;
|
||||
use Doctrine\Common\Cache\ArrayCache;
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use Doctrine\Common\Cache\CacheProvider;
|
||||
use Doctrine\ORM\Cache\CollectionCacheEntry;
|
||||
use Doctrine\ORM\Cache\Region\DefaultRegion;
|
||||
use Doctrine\Tests\Mocks\CacheEntryMock;
|
||||
@ -28,14 +28,11 @@ class DefaultRegionTest extends AbstractRegionTest
|
||||
|
||||
public function testSharedRegion()
|
||||
{
|
||||
if ( ! extension_loaded('apc') || false === @apc_cache_info()) {
|
||||
$this->markTestSkipped('The ' . __CLASS__ .' requires the use of APC');
|
||||
}
|
||||
|
||||
$cache = new SharedArrayCache();
|
||||
$key = new CacheKeyMock('key');
|
||||
$entry = new CacheEntryMock(['value' => 'foo']);
|
||||
$region1 = new DefaultRegion('region1', new ApcCache());
|
||||
$region2 = new DefaultRegion('region2', new ApcCache());
|
||||
$region1 = new DefaultRegion('region1', $cache->createChild());
|
||||
$region2 = new DefaultRegion('region2', $cache->createChild());
|
||||
|
||||
$this->assertFalse($region1->contains($key));
|
||||
$this->assertFalse($region2->contains($key));
|
||||
@ -99,3 +96,60 @@ class DefaultRegionTest extends AbstractRegionTest
|
||||
$this->assertEquals($value2, $actual[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache provider that offers child cache items (sharing the same array)
|
||||
*
|
||||
* Declared as a different class for readability purposes and kept in this file
|
||||
* to keep its monstrosity contained.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class SharedArrayCache extends ArrayCache
|
||||
{
|
||||
public function createChild(): Cache
|
||||
{
|
||||
return new class ($this) extends CacheProvider
|
||||
{
|
||||
/**
|
||||
* @var ArrayCache
|
||||
*/
|
||||
private $parent;
|
||||
|
||||
public function __construct(ArrayCache $parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
protected function doFetch($id)
|
||||
{
|
||||
return $this->parent->doFetch($id);
|
||||
}
|
||||
|
||||
protected function doContains($id)
|
||||
{
|
||||
return $this->parent->doContains($id);
|
||||
}
|
||||
|
||||
protected function doSave($id, $data, $lifeTime = 0)
|
||||
{
|
||||
return $this->parent->doSave($id, $data, $lifeTime);
|
||||
}
|
||||
|
||||
protected function doDelete($id)
|
||||
{
|
||||
return $this->parent->doDelete($id);
|
||||
}
|
||||
|
||||
protected function doFlush()
|
||||
{
|
||||
return $this->parent->doFlush();
|
||||
}
|
||||
|
||||
protected function doGetStats()
|
||||
{
|
||||
return $this->parent->doGetStats();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -179,6 +179,8 @@ class ConfigurationTest extends TestCase
|
||||
{
|
||||
$this->setProductionSettings();
|
||||
$this->configuration->ensureProductionSettings();
|
||||
|
||||
$this->addToAssertionCount(1);
|
||||
}
|
||||
|
||||
public function testEnsureProductionSettingsQueryCache()
|
||||
@ -263,8 +265,6 @@ class ConfigurationTest extends TestCase
|
||||
$this->assertSame(null, $this->configuration->getCustomStringFunction('NonExistingFunction'));
|
||||
$this->configuration->setCustomStringFunctions(['OtherFunctionName' => __CLASS__]);
|
||||
$this->assertSame(__CLASS__, $this->configuration->getCustomStringFunction('OtherFunctionName'));
|
||||
$this->expectException(ORMException::class);
|
||||
$this->configuration->addCustomStringFunction('concat', __CLASS__);
|
||||
}
|
||||
|
||||
public function testAddGetCustomNumericFunction()
|
||||
@ -274,8 +274,6 @@ class ConfigurationTest extends TestCase
|
||||
$this->assertSame(null, $this->configuration->getCustomNumericFunction('NonExistingFunction'));
|
||||
$this->configuration->setCustomNumericFunctions(['OtherFunctionName' => __CLASS__]);
|
||||
$this->assertSame(__CLASS__, $this->configuration->getCustomNumericFunction('OtherFunctionName'));
|
||||
$this->expectException(ORMException::class);
|
||||
$this->configuration->addCustomNumericFunction('abs', __CLASS__);
|
||||
}
|
||||
|
||||
public function testAddGetCustomDatetimeFunction()
|
||||
@ -285,8 +283,6 @@ class ConfigurationTest extends TestCase
|
||||
$this->assertSame(null, $this->configuration->getCustomDatetimeFunction('NonExistingFunction'));
|
||||
$this->configuration->setCustomDatetimeFunctions(['OtherFunctionName' => __CLASS__]);
|
||||
$this->assertSame(__CLASS__, $this->configuration->getCustomDatetimeFunction('OtherFunctionName'));
|
||||
$this->expectException(ORMException::class);
|
||||
$this->configuration->addCustomDatetimeFunction('date_add', __CLASS__);
|
||||
}
|
||||
|
||||
public function testAddGetCustomHydrationMode()
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user