Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/3.3.x' into 4.0.x
Browse files Browse the repository at this point in the history
  • Loading branch information
greg0ire committed Oct 9, 2024
2 parents 9b09cd0 + d297830 commit dbf26db
Show file tree
Hide file tree
Showing 75 changed files with 1,564 additions and 237 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/coding-standards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ on:

jobs:
coding-standards:
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.0.1"
uses: "doctrine/.github/.github/workflows/coding-standards.yml@5.1.0"
43 changes: 7 additions & 36 deletions .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,45 +5,16 @@ on:
branches:
- "*.x"
paths:
- .github/workflows/documentation.yml
- docs/**
- ".github/workflows/documentation.yml"
- "docs/**"
push:
branches:
- "*.x"
paths:
- .github/workflows/documentation.yml
- docs/**
- ".github/workflows/documentation.yml"
- "docs/**"

jobs:
validate-with-guides:
name: "Validate documentation with phpDocumentor/guides"
runs-on: "ubuntu-22.04"

steps:
- name: "Checkout code"
uses: "actions/checkout@v4"

- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
coverage: "none"
php-version: "8.3"

- name: "Remove existing composer file"
run: "rm composer.json"

- name: "Require phpdocumentor/guides-cli"
run: "composer require --dev phpdocumentor/guides-cli --no-update"

- name: "Install dependencies with Composer"
uses: "ramsey/composer-install@v3"
with:
dependency-versions: "highest"

- name: "Add orphan metadata where needed"
run: |
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/sidebar.rst)" > docs/en/sidebar.rst
printf '%s\n\n%s\n' ":orphan:" "$(cat docs/en/reference/installation.rst)" > docs/en/reference/installation.rst
- name: "Run guides-cli"
run: "vendor/bin/guides -vvv --no-progress docs/en 2>&1 | grep -v 'No template found for rendering directive' | ( ! grep WARNING )"
documentation:
name: "Documentation"
uses: "doctrine/.github/.github/workflows/[email protected]"
2 changes: 1 addition & 1 deletion .github/workflows/release-on-milestone-closed.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:

jobs:
release:
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.0.1"
uses: "doctrine/.github/.github/workflows/release-on-milestone-closed.yml@5.1.0"
secrets:
GIT_AUTHOR_EMAIL: ${{ secrets.GIT_AUTHOR_EMAIL }}
GIT_AUTHOR_NAME: ${{ secrets.GIT_AUTHOR_NAME }}
Expand Down
15 changes: 12 additions & 3 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,11 @@ now they throw an exception.

## BC BREAK: Partial objects are removed

- The `PARTIAL` keyword in DQL no longer exists.
- `Doctrine\ORM\Query\AST\PartialObjectExpression`is removed.
- `Doctrine\ORM\Query\SqlWalker::HINT_PARTIAL` and
WARNING: This was relaxed in ORM 3.2 when partial was re-allowed for array-hydration.

- The `PARTIAL` keyword in DQL no longer exists (reintroduced in ORM 3.2)
- `Doctrine\ORM\Query\AST\PartialObjectExpression` is removed. (reintroduced in ORM 3.2)
- `Doctrine\ORM\Query\SqlWalker::HINT_PARTIAL` (reintroduced in ORM 3.2) and
`Doctrine\ORM\Query::HINT_FORCE_PARTIAL_LOAD` are removed.
- `Doctrine\ORM\EntityManager*::getPartialReference()` is removed.

Expand Down Expand Up @@ -779,6 +781,13 @@ following classes and methods:

Use `toIterable()` instead.

# Upgrade to 2.20

## PARTIAL DQL syntax is undeprecated for non-object hydration

Use of the PARTIAL keyword is not deprecated anymore in DQL when used with a hydrator
that is not creating entities, such as the ArrayHydrator.

# Upgrade to 2.19

## Deprecate calling `ClassMetadata::getAssociationMappedByTargetField()` with the owning side of an association
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
"require-dev": {
"doctrine/coding-standard": "^12.0",
"phpbench/phpbench": "^1.0",
"phpstan/phpstan": "1.11.1",
"phpdocumentor/guides-cli": "^1.4",
"phpstan/phpstan": "1.12.0",
"phpunit/phpunit": "^10.4.0",
"psr/log": "^1 || ^2 || ^3",
"squizlabs/php_codesniffer": "3.7.2",
Expand Down
27 changes: 27 additions & 0 deletions docs/en/cookbook/dql-user-defined-functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,33 @@ vendors SQL parser to show us further errors in the parsing
process, for example if the Unit would not be one of the supported
values by MySql.

Typed functions
---------------
By default, result of custom functions is fetched as-is from the database driver.
If you want to be sure that the type is always the same, then your custom function needs to
implement ``Doctrine\ORM\Query\AST\TypedExpression``. Then, the result is wired
through ``Doctrine\DBAL\Types\Type::convertToPhpValue()`` of the ``Type`` returned in ``getReturnType()``.

.. code-block:: php
<?php
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\AST\TypedExpression;
class DateDiff extends FunctionNode implements TypedExpression
{
// ...
public function getReturnType(): Type
{
return Type::getType(Types::INTEGER);
}
}
Conclusion
----------

Expand Down
1 change: 1 addition & 0 deletions docs/en/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ Advanced Topics
* :doc:`TypedFieldMapper <reference/typedfieldmapper>`
* :doc:`Improving Performance <reference/improving-performance>`
* :doc:`Caching <reference/caching>`
* :doc:`Partial Hydration <reference/partial-hydration>`
* :doc:`Change Tracking Policies <reference/change-tracking-policies>`
* :doc:`Best Practices <reference/best-practices>`
* :doc:`Metadata Drivers <reference/metadata-drivers>`
Expand Down
2 changes: 1 addition & 1 deletion docs/en/reference/attributes-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Index
- :ref:`#[AttributeOverride] <attrref_attributeoverride>`
- :ref:`#[Column] <attrref_column>`
- :ref:`#[Cache] <attrref_cache>`
- :ref:`#[ChangeTrackingPolicy <attrref_changetrackingpolicy>`
- :ref:`#[ChangeTrackingPolicy] <attrref_changetrackingpolicy>`
- :ref:`#[CustomIdGenerator] <attrref_customidgenerator>`
- :ref:`#[DiscriminatorColumn] <attrref_discriminatorcolumn>`
- :ref:`#[DiscriminatorMap] <attrref_discriminatormap>`
Expand Down
54 changes: 51 additions & 3 deletions docs/en/reference/dql-doctrine-query-language.rst
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,25 @@ when the DQL is switched to an arbitrary join.
- HAVING is applied to the results of a query after
aggregation (GROUP BY)


Partial Hydration Syntax
^^^^^^^^^^^^^^^^^^^^^^^^

By default when you run a DQL query in Doctrine and select only a
subset of the fields for a given entity, you do not receive objects
back. Instead, you receive only arrays as a flat rectangular result
set, similar to how you would if you were just using SQL directly
and joining some data.

If you want to select a partial number of fields for hydration entity in
the context of array hydration and joins you can use the ``partial`` DQL keyword:

.. code-block:: php
<?php
$query = $em->createQuery('SELECT partial u.{id, username}, partial a.{id, name} FROM CmsUser u JOIN u.articles a');
$users = $query->getArrayResult(); // array of partially loaded CmsUser and CmsArticle fields
"NEW" Operator Syntax
^^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -560,7 +579,34 @@ And then use the ``NEW`` DQL keyword :
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, a.city, SUM(o.value)) FROM Customer c JOIN c.email e JOIN c.address a JOIN c.orders o GROUP BY c');
$users = $query->getResult(); // array of CustomerDTO
Note that you can only pass scalar expressions to the constructor.
You can also nest several DTO :

.. code-block:: php
<?php
class CustomerDTO
{
public function __construct(string $name, string $email, AddressDTO $address, string|null $value = null)
{
// Bind values to the object properties.
}
}
class AddressDTO
{
public function __construct(string $street, string $city, string $zip)
{
// Bind values to the object properties.
}
}
.. code-block:: php
<?php
$query = $em->createQuery('SELECT NEW CustomerDTO(c.name, e.email, NEW AddressDTO(a.street, a.city, a.zip)) FROM Customer c JOIN c.email e JOIN c.address a');
$users = $query->getResult(); // array of CustomerDTO
Note that you can only pass scalar expressions or other Data Transfer Objects to the constructor.

Using INDEX BY
~~~~~~~~~~~~~~
Expand Down Expand Up @@ -1576,10 +1622,12 @@ Select Expressions

.. code-block:: php
SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
SelectExpression ::= (IdentificationVariable | ScalarExpression | AggregateExpression | FunctionDeclaration | PartialObjectExpression | "(" Subselect ")" | CaseExpression | NewObjectExpression) [["AS"] ["HIDDEN"] AliasResultVariable]
SimpleSelectExpression ::= (StateFieldPathExpression | IdentificationVariable | FunctionDeclaration | AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable]
PartialObjectExpression ::= "PARTIAL" IdentificationVariable "." PartialFieldSet
PartialFieldSet ::= "{" SimpleStateField {"," SimpleStateField}* "}"
NewObjectExpression ::= "NEW" AbstractSchemaName "(" NewObjectArg {"," NewObjectArg}* ")"
NewObjectArg ::= ScalarExpression | "(" Subselect ")"
NewObjectArg ::= ScalarExpression | "(" Subselect ")" | NewObjectExpression
Conditional Expressions
~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
2 changes: 2 additions & 0 deletions docs/en/reference/installation.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
:orphan:

Installation
============

Expand Down
20 changes: 20 additions & 0 deletions docs/en/reference/partial-hydration.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
Partial Hydration
=================

.. note::

Creating Partial Objects through DQL was possible in ORM 2,
but is only supported for array hydration as of ORM 3.

Partial hydration of entities is allowed in the array hydrator, when
only a subset of the fields of an entity are loaded from the database
and the nested results are still created based on the entity relationship structure.

.. code-block:: php
<?php
$users = $em->createQuery("SELECT PARTIAL u.{id,name}, partial a.{id,street} FROM MyApp\Domain\User u JOIN u.addresses a")
->getArrayResult();
This is a useful optimization when you are not interested in all fields of an entity
for performance reasons, for example in use-cases for exporting or rendering lots of data.
18 changes: 18 additions & 0 deletions docs/en/reference/query-builder.rst
Original file line number Diff line number Diff line change
Expand Up @@ -611,3 +611,21 @@ same query of example 6 written using
->add('from', new Expr\From('User', 'u'))
->add('where', new Expr\Comparison('u.id', '=', '?1'))
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
Binding Parameters to Placeholders
----------------------------------

It is often not necessary to know about the exact placeholder names when
building a query. You can use a helper method to bind a value to a placeholder
and directly use that placeholder in your query as a return value:

.. code-block:: php
<?php
// $qb instanceof QueryBuilder
$qb->select('u')
->from('User', 'u')
->where('u.email = ' . $qb->createNamedParameter($userInputEmail))
;
// SELECT u FROM User u WHERE email = :dcValue1
9 changes: 5 additions & 4 deletions docs/en/reference/working-with-objects.rst
Original file line number Diff line number Diff line change
Expand Up @@ -338,10 +338,11 @@ Performance of different deletion strategies
Deleting an object with all its associated objects can be achieved
in multiple ways with very different performance impacts.

1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM
will fetch this association. If its a Single association it will
pass this entity to
``EntityManager#remove()``. If the association is a collection, Doctrine will loop over all its elements and pass them to``EntityManager#remove()``.
1. If an association is marked as ``CASCADE=REMOVE`` Doctrine ORM will
fetch this association. If it's a Single association it will pass
this entity to ``EntityManager#remove()``. If the association is a
collection, Doctrine will loop over all its elements and pass them to
``EntityManager#remove()``.
In both cases the cascade remove semantics are applied recursively.
For large object graphs this removal strategy can be very costly.
2. Using a DQL ``DELETE`` statement allows you to delete multiple
Expand Down
Loading

0 comments on commit dbf26db

Please sign in to comment.