diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 1b7e44bc95..6488653c45 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -17,7 +17,6 @@ jobs: strategy: matrix: php-version: - - "7.4" - "8.0" - "8.1" - "8.2" @@ -33,7 +32,7 @@ jobs: - "highest" include: - dependencies: "lowest" - php-version: "7.4" + php-version: "8.0" mongodb-version: "4.4" driver-version: "1.11.0" topology: "server" diff --git a/.github/workflows/performance.yml b/.github/workflows/performance.yml index 92a583125b..94c99bce9a 100644 --- a/.github/workflows/performance.yml +++ b/.github/workflows/performance.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: php-version: - - "7.4" + - "8.0" services: mongodb: diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 2f0f81c7ef..2429258862 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: php-version: - - "8.1" + - "8.2" steps: - name: "Checkout code" @@ -88,4 +88,4 @@ jobs: path: composer.lock - name: "Run a static analysis with vimeo/psalm" - run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc) --php-version=${{ matrix.php-version }}" + run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)" diff --git a/.gitignore b/.gitignore index 68a3971619..ef4553f5df 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ vendor/ .phpunit.cache .phpunit.result.cache phpcs.xml +psalm.xml diff --git a/UPGRADE-2.6.md b/UPGRADE-2.6.md new file mode 100644 index 0000000000..8a69e33536 --- /dev/null +++ b/UPGRADE-2.6.md @@ -0,0 +1,6 @@ +# UPGRADE FROM to 2.6 + +## PHP requirements + +* MongoDB ODM 2.6 requires PHP 8.0 or newer. If you're not running PHP 8.0 yet, + it's recommended that you upgrade to PHP 8.0 before upgrading ODM. diff --git a/composer.json b/composer.json index 11c2ef3d64..35092a7b1c 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ { "name": "Fran Moreno", "email": "franmomu@gmail.com" } ], "require": { - "php": "^7.4 || ^8.0", + "php": "^8.0", "ext-mongodb": "^1.11", "doctrine/annotations": "^1.12 || ^2.0", "doctrine/cache": "^1.11 || ^2.0", diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Aggregation.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Aggregation.php index 655de73d29..b1d2a6955e 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Aggregation.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Aggregation.php @@ -17,34 +17,16 @@ use function array_merge; use function assert; +/** @psalm-import-type PipelineExpression from Builder */ final class Aggregation implements IteratorAggregate { - private DocumentManager $dm; - - private ?ClassMetadata $classMetadata; - - private Collection $collection; - - /** @var array */ - private array $pipeline; - - /** @var array */ - private array $options; - - private bool $rewindable; - /** * @param array $pipeline * @param array $options + * @psalm-param PipelineExpression $pipeline */ - public function __construct(DocumentManager $dm, ?ClassMetadata $classMetadata, Collection $collection, array $pipeline, array $options = [], bool $rewindable = true) + public function __construct(private DocumentManager $dm, private ?ClassMetadata $classMetadata, private Collection $collection, private array $pipeline, private array $options = [], private bool $rewindable = true) { - $this->dm = $dm; - $this->classMetadata = $classMetadata; - $this->collection = $collection; - $this->pipeline = $pipeline; - $this->options = $options; - $this->rewindable = $rewindable; } public function getIterator(): Iterator diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php index 5a0f785771..8dc6fb9bed 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php @@ -29,6 +29,8 @@ * Fluent interface for building aggregation pipelines. * * @psalm-import-type SortShape from Sort + * @psalm-import-type StageExpression from Stage + * @psalm-type PipelineExpression = list */ class Builder { @@ -83,9 +85,8 @@ public function __construct(DocumentManager $dm, string $documentName) public function addFields(): Stage\AddFields { $stage = new Stage\AddFields($this); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -103,9 +104,8 @@ public function addFields(): Stage\AddFields public function bucket(): Stage\Bucket { $stage = new Stage\Bucket($this, $this->dm, $this->class); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -125,9 +125,8 @@ public function bucket(): Stage\Bucket public function bucketAuto(): Stage\BucketAuto { $stage = new Stage\BucketAuto($this, $this->dm, $this->class); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -141,9 +140,8 @@ public function bucketAuto(): Stage\BucketAuto public function collStats(): Stage\CollStats { $stage = new Stage\CollStats($this); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -155,9 +153,20 @@ public function collStats(): Stage\CollStats public function count(string $fieldName): Stage\Count { $stage = new Stage\Count($this, $fieldName); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); + } + + /** + * Creates new documents in a sequence of documents where certain values in a field are missing. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/densify/ + */ + public function densify(string $fieldName): Stage\Densify + { + $stage = new Stage\Densify($this, $fieldName); + + return $this->addStage($stage); } /** @@ -195,9 +204,20 @@ public function expr(): Expr public function facet(): Stage\Facet { $stage = new Stage\Facet($this); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); + } + + /** + * Populates null and missing field values within documents. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/fill/ + */ + public function fill(): Stage\Fill + { + $stage = new Stage\Fill($this); + + return $this->addStage($stage); } /** @@ -218,9 +238,8 @@ public function facet(): Stage\Facet public function geoNear($x, $y = null): Stage\GeoNear { $stage = new Stage\GeoNear($this, $x, $y); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -252,6 +271,7 @@ public function getAggregation(array $options = []): Aggregation * given. * * @return array> + * @psalm-return PipelineExpression */ // phpcs:enable Squiz.Commenting.FunctionComment.ExtraParamComment public function getPipeline(/* bool $applyFilters = true */): array @@ -321,9 +341,8 @@ public function getStage(int $index): Stage public function graphLookup(string $from): Stage\GraphLookup { $stage = new Stage\GraphLookup($this, $from, $this->dm, $this->class); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -335,15 +354,14 @@ public function graphLookup(string $from): Stage\GraphLookup public function group(): Stage\Group { $stage = new Stage\Group($this); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** * Set which class to use when hydrating results as document class instances. */ - public function hydrate(?string $className): self + public function hydrate(?string $className): static { $this->hydrationClass = $className; @@ -358,9 +376,8 @@ public function hydrate(?string $className): self public function indexStats(): Stage\IndexStats { $stage = new Stage\IndexStats($this); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -371,9 +388,8 @@ public function indexStats(): Stage\IndexStats public function limit(int $limit): Stage\Limit { $stage = new Stage\Limit($this, $limit); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -386,9 +402,8 @@ public function limit(int $limit): Stage\Limit public function lookup(string $from): Stage\Lookup { $stage = new Stage\Lookup($this, $from, $this->dm, $this->class); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -400,9 +415,8 @@ public function lookup(string $from): Stage\Lookup public function match(): Stage\MatchStage { $stage = new Stage\MatchStage($this); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -416,6 +430,19 @@ public function matchExpr(): QueryExpr return $expr; } + /** + * Writes the results of the aggregation pipeline to a specified collection. + * The $merge operator must be the last stage in the pipeline. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/merge/ + */ + public function merge(): Stage\Merge + { + $stage = new Stage\Merge($this, $this->dm); + + return $this->addStage($stage); + } + /** * Takes the documents returned by the aggregation pipeline and writes them * to a specified collection. This must be the last stage in the pipeline. @@ -425,9 +452,8 @@ public function matchExpr(): QueryExpr public function out(string $from): Stage\Out { $stage = new Stage\Out($this, $from, $this->dm); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -440,9 +466,8 @@ public function out(string $from): Stage\Out public function project(): Stage\Project { $stage = new Stage\Project($this); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -454,9 +479,8 @@ public function project(): Stage\Project public function redact(): Stage\Redact { $stage = new Stage\Redact($this); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -473,15 +497,34 @@ public function redact(): Stage\Redact public function replaceRoot($expression = null): Stage\ReplaceRoot { $stage = new Stage\ReplaceRoot($this, $this->dm, $this->class, $expression); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); + } + + /** + * Replaces the input document with the specified document. The operation + * replaces all existing fields in the input document, including the _id + * field. With $replaceWith, you can promote an embedded document to the + * top-level. You can also specify a new document as the replacement. + * + * The $replaceWith stage is an alias for $replaceRoot. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/replaceWith/ + * + * @param string|mixed[]|Expr|null $expression Optional. A replacement expression that + * resolves to a document. + */ + public function replaceWith($expression = null): Stage\ReplaceWith + { + $stage = new Stage\ReplaceWith($this, $this->dm, $this->class, $expression); + + return $this->addStage($stage); } /** * Controls if resulting iterator should be wrapped with CachingIterator. */ - public function rewindable(bool $rewindable = true): self + public function rewindable(bool $rewindable = true): static { $this->rewindable = $rewindable; @@ -496,9 +539,36 @@ public function rewindable(bool $rewindable = true): self public function sample(int $size): Stage\Sample { $stage = new Stage\Sample($this, $size); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); + } + + /** + * The $search stage performs a full-text search on the specified field or + * fields which must be covered by an Atlas Search index. + * + * @see https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/#mongodb-pipeline-pipe.-search + */ + public function search(): Stage\Search + { + $stage = new Stage\Search($this); + + return $this->addStage($stage); + } + + /** + * Adds new fields to documents. $set outputs documents that contain all + * existing fields from the input documents and newly added fields. + * + * The $set stage is an alias for $addFields. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/set/ + */ + public function set(): Stage\Set + { + $stage = new Stage\Set($this); + + return $this->addStage($stage); } /** @@ -510,9 +580,8 @@ public function sample(int $size): Stage\Sample public function skip(int $skip): Stage\Skip { $stage = new Stage\Skip($this, $skip); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -533,9 +602,8 @@ public function sort($fieldName, $order = null): Stage\Sort $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order]; // fixme: move to sort stage $stage = new Stage\Sort($this, $this->getDocumentPersister()->prepareSort($fields)); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** @@ -547,9 +615,34 @@ public function sort($fieldName, $order = null): Stage\Sort public function sortByCount(string $expression): Stage\SortByCount { $stage = new Stage\SortByCount($this, $expression, $this->dm, $this->class); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); + } + + /** + * Performs a union of two collections. $unionWith combines pipeline results + * from two collections into a single result set. The stage outputs the + * combined result set (including duplicates) to the next stage. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/unionWith/ + */ + public function unionWith(string $collection): Stage\UnionWith + { + $stage = new Stage\UnionWith($this, $this->dm, $collection); + + return $this->addStage($stage); + } + + /** + * Removes/excludes fields from documents. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/unset/ + */ + public function unset(string ...$fields): Stage\UnsetStage + { + $stage = new Stage\UnsetStage($this, $this->getDocumentPersister(), ...$fields); + + return $this->addStage($stage); } /** @@ -563,15 +656,18 @@ public function unwind(string $fieldName): Stage\Unwind { // Fixme: move field name translation to stage $stage = new Stage\Unwind($this, $this->getDocumentPersister()->prepareFieldName($fieldName)); - $this->addStage($stage); - return $stage; + return $this->addStage($stage); } /** * Allows adding an arbitrary stage to the pipeline * - * @return Stage The method returns the stage given as an argument + * @param T $stage + * + * @return T The method returns the stage given as an argument + * + * @template T of Stage */ public function addStage(Stage $stage): Stage { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php index 32cf7d1f5f..c933202f7b 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php @@ -5,31 +5,66 @@ namespace Doctrine\ODM\MongoDB\Aggregation; use BadMethodCallException; -use Doctrine\ODM\MongoDB\Aggregation\Operator\GenericOperatorsInterface; +use Doctrine\ODM\MongoDB\Aggregation\Operator\AccumulatorOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ArithmeticOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ArrayOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\BooleanOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ComparisonOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ConditionalOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\CustomOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\DataSizeOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\DateOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\GroupAccumulatorOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\MiscOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ObjectOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\SetOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\StringOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\TimestampOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\TrigonometryOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\TypeOperators; use Doctrine\ODM\MongoDB\DocumentManager; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Doctrine\ODM\MongoDB\Persisters\DocumentPersister; use Doctrine\ODM\MongoDB\Types\Type; -use Doctrine\Persistence\Mapping\ClassMetadata as ClassMetadataInterface; use LogicException; +use function array_filter; use function array_map; use function array_merge; -use function assert; +use function count; use function func_get_args; +use function in_array; use function is_array; use function is_string; +use function sprintf; use function substr; +use const ARRAY_FILTER_USE_BOTH; + /** * Fluent interface for building aggregation pipelines. + * + * @psalm-type OperatorExpression = array|object */ -class Expr implements GenericOperatorsInterface +class Expr implements + AccumulatorOperators, + ArithmeticOperators, + ArrayOperators, + BooleanOperators, + ComparisonOperators, + ConditionalOperators, + CustomOperators, + DataSizeOperators, + DateOperators, + GroupAccumulatorOperators, + MiscOperators, + ObjectOperators, + SetOperators, + StringOperators, + TimestampOperators, + TrigonometryOperators, + TypeOperators { - private DocumentManager $dm; - - private ClassMetadata $class; - /** @var array */ private array $expr = []; @@ -41,43 +76,35 @@ class Expr implements GenericOperatorsInterface /** @var array{case: mixed|self, then?: mixed|self}|null */ private ?array $switchBranch = null; - public function __construct(DocumentManager $dm, ClassMetadataInterface $class) + public function __construct(private DocumentManager $dm, private ClassMetadata $class) { - assert($class instanceof ClassMetadata); - $this->dm = $dm; - $this->class = $class; } - /** - * Returns the absolute value of a number. - * - * The argument can be any valid expression as long as it resolves - * to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/abs/ - * - * @param mixed|self $number - */ - public function abs($number): self + public function abs($number): static { return $this->operator('$abs', $number); } - /** - * Adds numbers together or adds numbers and a date. If one of the arguments - * is a date, $add treats the other arguments as milliseconds to add to the - * date. - * - * The arguments can be any valid expression as long as they resolve to - * either all numbers or to numbers and a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/add/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - * @param mixed|self ...$expressions Additional expressions - */ - public function add($expression1, $expression2, ...$expressions): self + public function accumulator($init, $accumulate, $accumulateArgs, $merge, $initArgs = null, $finalize = null, $lang = 'js'): static + { + return $this->operator( + '$accumulator', + $this->filterOptionalNullArguments( + [ + 'init' => $init, + 'initArgs' => $initArgs, + 'accumulate' => $accumulate, + 'accumulateArgs' => $accumulateArgs, + 'merge' => $merge, + 'finalize' => $finalize, + 'lang' => $lang, + ], + ['initArgs', 'finalize'], + ), + ); + } + + public function add($expression1, $expression2, ...$expressions): static { return $this->operator('$add', func_get_args()); } @@ -87,16 +114,16 @@ public function add($expression1, $expression2, ...$expressions): self * * @see https://docs.mongodb.com/manual/reference/operator/aggregation/and/ * - * @param array|self $expression - * @param array|self ...$expressions + * @param array|Expr $expression + * @param array|Expr ...$expressions */ - public function addAnd($expression, ...$expressions): self + public function addAnd($expression, ...$expressions): static { if (! isset($this->expr['$and'])) { $this->expr['$and'] = []; } - $this->expr['$and'] = array_merge($this->expr['$and'], array_map([$this, 'ensureArray'], func_get_args())); + $this->expr['$and'] = array_merge($this->expr['$and'], array_map([$this, 'prepareArgument'], func_get_args())); return $this; } @@ -106,108 +133,71 @@ public function addAnd($expression, ...$expressions): self * * @see https://docs.mongodb.com/manual/reference/operator/aggregation/or/ * - * @param array|self $expression - * @param array|self ...$expressions + * @param array|Expr $expression + * @param array|Expr ...$expressions */ - public function addOr($expression, ...$expressions): self + public function addOr($expression, ...$expressions): static { if (! isset($this->expr['$or'])) { $this->expr['$or'] = []; } - $this->expr['$or'] = array_merge($this->expr['$or'], array_map([$this, 'ensureArray'], func_get_args())); + $this->expr['$or'] = array_merge($this->expr['$or'], array_map([$this, 'prepareArgument'], func_get_args())); return $this; } - /** - * Returns an array of all unique values that results from applying an - * expression to each document in a group of documents that share the same - * group by key. Order of the elements in the output array is unspecified. - * - * AddToSet is an accumulator operation only available in the group stage. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/addToSet/ - * - * @param mixed|self $expression - */ - public function addToSet($expression): self + public function addToSet($expression): static { return $this->operator('$addToSet', $expression); } - /** - * Evaluates an array as a set and returns true if no element in the array - * is false. Otherwise, returns false. An empty array returns true. - * - * The expression must resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/allElementsTrue/ - * - * @param mixed|self $expression - */ - public function allElementsTrue($expression): self + public function allElementsTrue($expression): static { return $this->operator('$allElementsTrue', $expression); } - /** - * Evaluates an array as a set and returns true if any of the elements are - * true and false otherwise. An empty array returns false. - * - * The expression must resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/anyElementTrue/ - * - * @param mixed[]|self $expression - */ - public function anyElementTrue($expression): self + public function and($expression, ...$expressions): static + { + return $this->operator('$and', func_get_args()); + } + + public function anyElementTrue($expression): static { return $this->operator('$anyElementTrue', $expression); } - /** - * Returns the element at the specified array index. - * - * The expression can be any valid expression as long as it resolves - * to an array. - * The expression can be any valid expression as long as it resolves - * to an integer. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayElemAt/ - * - * @param mixed|self $array - * @param mixed|self $index - */ - public function arrayElemAt($array, $index): self + public function arrayElemAt($array, $index): static { - return $this->operator('$arrayElemAt', [$array, $index]); + return $this->operator('$arrayElemAt', func_get_args()); } - /** - * Returns the average value of the numeric values that result from applying - * a specified expression to each document in a group of documents that - * share the same group by key. Ignores nun-numeric values. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/avg/ - * - * @param mixed|self $expression - */ - public function avg($expression): self + public function avg($expression, ...$expressions): static { - return $this->operator('$avg', $expression); + return $this->accumulatorOperator('$avg', ...func_get_args()); } - /** - * Adds a case statement for a branch of the $switch operator. - * - * Requires {@link switch()} to be called first. The argument can be any - * valid expression that resolves to a boolean. If the result is not a - * boolean, it is coerced to a boolean value. - * - * @param mixed|self $expression - */ - public function case($expression): self + public function binarySize($expression): static + { + return $this->operator('$binarySize', $expression); + } + + public function bottom($output, $sortBy): static + { + return $this->operator('$bottom', ['output' => $output, 'sortBy' => $sortBy]); + } + + public function bottomN($output, $sortBy, $n): static + { + return $this->operator('$bottomN', ['output' => $output, 'sortBy' => $sortBy, 'n' => $n]); + } + + public function bsonSize($expression): static + { + return $this->operator('$bsonSize', $expression); + } + + public function case($expression): static { $this->requiresSwitchStatement(static::class . '::case'); @@ -216,85 +206,27 @@ public function case($expression): self return $this; } - /** - * Returns the smallest integer greater than or equal to the specified number. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ceil/ - * - * @param mixed|self $number - */ - public function ceil($number): self + public function ceil($number): static { return $this->operator('$ceil', $number); } - /** - * Compares two values and returns: - * -1 if the first value is less than the second. - * 1 if the first value is greater than the second. - * 0 if the two values are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cmp/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function cmp($expression1, $expression2): self + public function cmp($expression1, $expression2): static { - return $this->operator('$cmp', [$expression1, $expression2]); + return $this->operator('$cmp', func_get_args()); } - /** - * Concatenates strings and returns the concatenated string. - * - * The arguments can be any valid expression as long as they resolve to - * strings. If the argument resolves to a value of null or refers to a field - * that is missing, $concat returns null. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/concat/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - * @param mixed|self ...$expressions Additional expressions - */ - public function concat($expression1, $expression2, ...$expressions): self + public function concat($expression1, $expression2, ...$expressions): static { return $this->operator('$concat', func_get_args()); } - /** - * Concatenates arrays to return the concatenated array. - * - * The expressions can be any valid expression as long as they - * resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/concatArrays/ - * - * @param mixed|self $array1 - * @param mixed|self $array2 - * @param mixed|self ...$arrays Additional expressions - */ - public function concatArrays($array1, $array2, ...$arrays): self + public function concatArrays($array1, $array2, ...$arrays): static { return $this->operator('$concatArrays', func_get_args()); } - /** - * Evaluates a boolean expression to return one of the two specified return - * expressions. - * - * The arguments can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cond/ - * - * @param mixed|self $if - * @param mixed|self $then - * @param mixed|self $else - */ - public function cond($if, $then, $else): self + public function cond($if, $then, $else): static { return $this->operator('$cond', ['if' => $if, 'then' => $then, 'else' => $else]); } @@ -325,129 +257,199 @@ public static function convertExpression($expression) return $expression; } - /** - * Converts a date object to a string according to a user-specified format. - * - * The format string can be any string literal, containing 0 or more format - * specifiers. - * The date argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dateToString/ - * - * @param mixed|self $expression - */ - public function dateToString(string $format, $expression): self - { - return $this->operator('$dateToString', ['format' => $format, 'date' => $expression]); - } - - /** - * Returns the day of the month for a date as a number between 1 and 31. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfMonth/ - * - * @param mixed|self $expression - */ - public function dayOfMonth($expression): self + public function countDocuments(): static + { + return $this->operator('$count', []); + } + + public function dateAdd($startDate, $unit, $amount, $timezone = null): static + { + return $this->operator( + '$dateAdd', + $this->filterOptionalNullArguments( + [ + 'startDate' => $startDate, + 'unit' => $unit, + 'amount' => $amount, + 'timezone' => $timezone, + ], + ['timezone'], + ), + ); + } + + public function dateDiff($startDate, $endDate, $unit, $timezone = null, $startOfWeek = null): static + { + return $this->operator( + '$dateDiff', + $this->filterOptionalNullArguments( + [ + 'startDate' => $startDate, + 'endDate' => $endDate, + 'unit' => $unit, + 'timezone' => $timezone, + 'startOfWeek' => $startOfWeek, + ], + ['timezone', 'startOfWeek'], + ), + ); + } + + public function dateFromParts($year = null, $isoWeekYear = null, $month = null, $isoWeek = null, $day = null, $isoDayOfWeek = null, $hour = null, $minute = null, $second = null, $millisecond = null, $timezone = null): static + { + return $this->operator( + '$dateFromParts', + $this->filterOptionalNullArguments( + [ + 'year' => $year, + 'isoWeekYear' => $isoWeekYear, + 'month' => $month, + 'isoWeek' => $isoWeek, + 'day' => $day, + 'isoDayOfWeek' => $isoDayOfWeek, + 'hour' => $hour, + 'minute' => $minute, + 'second' => $second, + 'millisecond' => $millisecond, + 'timezone' => $timezone, + ], + [ + 'year', + 'isoWeekYear', + 'month', + 'isoWeek', + 'day', + 'isoDayOfWeek', + 'hour', + 'minute', + 'second', + 'millisecond', + 'timezone', + ], + ), + ); + } + + public function dateFromString($dateString, $format = null, $timezone = null, $onError = null, $onNull = null): static + { + return $this->operator( + '$dateFromString', + $this->filterOptionalNullArguments( + [ + 'dateString' => $dateString, + 'format' => $format, + 'timezone' => $timezone, + 'onError' => $onError, + 'onNull' => $onNull, + ], + ['format', 'timezone', 'onError', 'onNull'], + ), + ); + } + + public function dateSubtract($startDate, $unit, $amount, $timezone = null): static + { + return $this->operator( + '$dateSubtract', + $this->filterOptionalNullArguments( + [ + 'startDate' => $startDate, + 'unit' => $unit, + 'amount' => $amount, + 'timezone' => $timezone, + ], + ['timezone'], + ), + ); + } + + public function dateToParts($date, $timezone = null, $iso8601 = null): static + { + return $this->operator( + '$dateToParts', + $this->filterOptionalNullArguments( + [ + 'date' => $date, + 'timezone' => $timezone, + 'iso8601' => $iso8601, + ], + ['timezone', 'iso8601'], + ), + ); + } + + public function dateToString(string $format, $expression, $timezone = null, $onNull = null): static + { + return $this->operator( + '$dateToString', + $this->filterOptionalNullArguments( + [ + 'date' => $expression, + 'format' => $format, + 'timezone' => $timezone, + 'onNull' => $onNull, + ], + ['timezone', 'onNull'], + ), + ); + } + + public function dateTrunc($date, $unit, $binSize = null, $timezone = null, $startOfWeek = null): static + { + return $this->operator( + '$dateTrunc', + $this->filterOptionalNullArguments( + [ + 'date' => $date, + 'unit' => $unit, + 'binSize' => $binSize, + 'timezone' => $timezone, + 'startOfWeek' => $startOfWeek, + ], + ['binSize', 'timezone', 'startOfWeek'], + ), + ); + } + + public function dayOfMonth($expression): static { return $this->operator('$dayOfMonth', $expression); } - /** - * Returns the day of the week for a date as a number between 1 (Sunday) and - * 7 (Saturday). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfWeek/ - * - * @param mixed|self $expression - */ - public function dayOfWeek($expression): self + public function dayOfWeek($expression): static { return $this->operator('$dayOfWeek', $expression); } - /** - * Returns the day of the year for a date as a number between 1 and 366. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfYear/ - * - * @param mixed|self $expression - */ - public function dayOfYear($expression): self + public function dayOfYear($expression): static { return $this->operator('$dayOfYear', $expression); } - /** - * Adds a default statement for the current $switch operator. - * - * Requires {@link switch()} to be called first. The argument can be any - * valid expression. - * - * Note: if no default is specified and no branch evaluates to true, the - * $switch operator throws an error. - * - * @param mixed|self $expression - */ - public function default($expression): self + public function default($expression): static { $this->requiresSwitchStatement(static::class . '::default'); if ($this->currentField) { - $this->expr[$this->currentField]['$switch']['default'] = $this->ensureArray($expression); + $this->expr[$this->currentField]['$switch']['default'] = $this->prepareArgument($expression); } else { - $this->expr['$switch']['default'] = $this->ensureArray($expression); + $this->expr['$switch']['default'] = $this->prepareArgument($expression); } return $this; } - /** - * Divides one number by another and returns the result. The first argument - * is divided by the second argument. - * - * The arguments can be any valid expression as long as the resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/divide/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function divide($expression1, $expression2): self + public function divide($expression1, $expression2): static { - return $this->operator('$divide', [$expression1, $expression2]); + return $this->operator('$divide', func_get_args()); } - /** - * Compares two values and returns whether the are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/eq/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function eq($expression1, $expression2): self + public function eq($expression1, $expression2): static { - return $this->operator('$eq', [$expression1, $expression2]); + return $this->operator('$eq', func_get_args()); } - /** - * Raises Euler’s number to the specified exponent and returns the result. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/exp/ - * - * @param mixed|self $exponent - */ - public function exp($exponent): self + public function exp($exponent): static { return $this->operator('$exp', $exponent); } @@ -455,22 +457,18 @@ public function exp($exponent): self /** * Returns a new expression object. */ - public function expr(): self + public function expr(): static { return new static($this->dm, $this->class); } - /** - * Allows any expression to be used as a field value. - * - * @see https://docs.mongodb.com/manual/meta/aggregation-quick-reference/#aggregation-expressions - * - * @param mixed|self $value - */ - public function expression($value): self + public function expression($value): static { - $this->requiresCurrentField(__METHOD__); - $this->expr[$this->currentField] = $this->ensureArray($value); + if (! $this->currentField) { + throw new LogicException(sprintf('%s requires setting a current field using field().', __METHOD__)); + } + + $this->expr[$this->currentField] = $this->prepareArgument($value); return $this; } @@ -478,7 +476,7 @@ public function expression($value): self /** * Set the current field for building the expression. */ - public function field(string $fieldName): self + public function field(string $fieldName): static { $fieldName = $this->getDocumentPersister()->prepareFieldName($fieldName); $this->currentField = $fieldName; @@ -486,48 +484,30 @@ public function field(string $fieldName): self return $this; } - /** - * Selects a subset of the array to return based on the specified condition. - * - * Returns an array with only those elements that match the condition. The - * returned elements are in the original order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/filter/ - * - * @param mixed|self $input - * @param mixed|self $as - * @param mixed|self $cond - */ - public function filter($input, $as, $cond): self + public function filter($input, $as, $cond): static { return $this->operator('$filter', ['input' => $input, 'as' => $as, 'cond' => $cond]); } - /** - * Returns the value that results from applying an expression to the first - * document in a group of documents that share the same group by key. Only - * meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/first/ - * - * @param mixed|self $expression - */ - public function first($expression): self + public function first($expression): static { return $this->operator('$first', $expression); } - /** - * Returns the largest integer less than or equal to the specified number. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/floor/ - * - * @param mixed|self $number - */ - public function floor($number): self + public function firstN($expression, $n): static + { + return $this->operator('$firstN', [ + 'input' => $expression, + 'n' => $n, + ]); + } + + public function function($body, $args, $lang = 'js'): static + { + return $this->operator('$function', ['body' => $body, 'args' => $args, 'lang' => $lang]); + } + + public function floor($number): static { return $this->operator('$floor', $number); } @@ -538,99 +518,46 @@ public function getExpression(): array return $this->expr; } - /** - * Compares two values and returns: - * true when the first value is greater than the second value. - * false when the first value is less than or equivalent to the second - * value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/gt/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function gt($expression1, $expression2): self + public function getField($field, $input = null): static { - return $this->operator('$gt', [$expression1, $expression2]); + return $this->operator( + '$getField', + $this->filterOptionalNullArguments( + [ + 'field' => $field, + 'input' => $input, + ], + ['input'], + ), + ); } - /** - * Compares two values and returns: - * true when the first value is greater than or equivalent to the second - * value. - * false when the first value is less than the second value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/gte/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function gte($expression1, $expression2): self + public function gt($expression1, $expression2): static { - return $this->operator('$gte', [$expression1, $expression2]); + return $this->operator('$gt', func_get_args()); } - /** - * Returns the hour portion of a date as a number between 0 and 23. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/hour/ - * - * @param mixed|self $expression - */ - public function hour($expression): self + public function gte($expression1, $expression2): static + { + return $this->operator('$gte', func_get_args()); + } + + public function hour($expression): static { return $this->operator('$hour', $expression); } - /** - * Evaluates an expression and returns the value of the expression if the - * expression evaluates to a non-null value. If the expression evaluates to - * a null value, including instances of undefined values or missing fields, - * returns the value of the replacement expression. - * - * The arguments can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/ - * - * @param mixed|self $expression - * @param mixed|self $replacementExpression - */ - public function ifNull($expression, $replacementExpression): self + public function ifNull($expression, $replacementExpression): static { - return $this->operator('$ifNull', [$expression, $replacementExpression]); + return $this->operator('$ifNull', func_get_args()); } - /** - * Returns a boolean indicating whether a specified value is in an array. - * - * Unlike the $in query operator, the aggregation $in operator does not - * support matching by regular expressions. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/in/ - * - * @param mixed|self $expression - * @param mixed|self $arrayExpression - */ - public function in($expression, $arrayExpression): self + public function in($expression, $arrayExpression): static { - return $this->operator('$in', [$expression, $arrayExpression]); + return $this->operator('$in', func_get_args()); } - /** - * Searches an array for an occurrence of a specified value and returns the - * array index (zero-based) of the first occurrence. If the value is not - * found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfArray/ - * - * @param mixed|self $arrayExpression can be any valid expression as long as it resolves to an array - * @param mixed|self $searchExpression can be any valid expression - * @param mixed|self $start Optional. An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param mixed|self $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - */ - public function indexOfArray($arrayExpression, $searchExpression, $start = null, $end = null): self + public function indexOfArray($arrayExpression, $searchExpression, $start = null, $end = null): static { $args = [$arrayExpression, $searchExpression]; if ($start !== null) { @@ -644,19 +571,7 @@ public function indexOfArray($arrayExpression, $searchExpression, $start = null, return $this->operator('$indexOfArray', $args); } - /** - * Searches a string for an occurrence of a substring and returns the UTF-8 - * byte index (zero-based) of the first occurrence. If the substring is not - * found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfBytes/ - * - * @param mixed|self $stringExpression can be any valid expression as long as it resolves to a string - * @param mixed|self $substringExpression can be any valid expression as long as it resolves to a string - * @param string|int|null $start An integral number that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param string|int|null $end An integral number that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - */ - public function indexOfBytes($stringExpression, $substringExpression, $start = null, $end = null): self + public function indexOfBytes($stringExpression, $substringExpression, $start = null, $end = null): static { $args = [$stringExpression, $substringExpression]; if ($start !== null) { @@ -670,19 +585,7 @@ public function indexOfBytes($stringExpression, $substringExpression, $start = n return $this->operator('$indexOfBytes', $args); } - /** - * Searches a string for an occurrence of a substring and returns the UTF-8 - * code point index (zero-based) of the first occurrence. If the substring is - * not found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfCP/ - * - * @param mixed|self $stringExpression can be any valid expression as long as it resolves to a string - * @param mixed|self $substringExpression can be any valid expression as long as it resolves to a string - * @param string|int|null $start An integral number that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param string|int|null $end An integral number that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - */ - public function indexOfCP($stringExpression, $substringExpression, $start = null, $end = null): self + public function indexOfCP($stringExpression, $substringExpression, $start = null, $end = null): static { $args = [$stringExpression, $substringExpression]; if ($start !== null) { @@ -696,935 +599,398 @@ public function indexOfCP($stringExpression, $substringExpression, $start = null return $this->operator('$indexOfCP', $args); } - /** - * Determines if the operand is an array. Returns a boolean. - * - * The can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isArray/ - * - * @param mixed|self $expression - */ - public function isArray($expression): self + public function isArray($expression): static { return $this->operator('$isArray', $expression); } - /** - * Returns the weekday number in ISO 8601 format, ranging from 1 (for Monday) - * to 7 (for Sunday). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoDayOfWeek/ - * - * @param mixed|self $expression - */ - public function isoDayOfWeek($expression): self + public function isoDayOfWeek($expression): static { return $this->operator('$isoDayOfWeek', $expression); } - /** - * Returns the week number in ISO 8601 format, ranging from 1 to 53. - * - * Week numbers start at 1 with the week (Monday through Sunday) that - * contains the year’s first Thursday. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoWeek/ - * - * @param mixed|self $expression - */ - public function isoWeek($expression): self + public function isoWeek($expression): static { return $this->operator('$isoWeek', $expression); } - /** - * Returns the year number in ISO 8601 format. - * - * The year starts with the Monday of week 1 (ISO 8601) and ends with the - * Sunday of the last week (ISO 8601). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoWeek/ - * - * @param mixed|self $expression - */ - public function isoWeekYear($expression): self + public function isoWeekYear($expression): static { return $this->operator('$isoWeekYear', $expression); } - /** - * Returns the value that results from applying an expression to the last - * document in a group of documents that share the same group by a field. - * Only meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/last/ - * - * @param mixed|self $expression - */ - public function last($expression): self + public function last($expression): static { return $this->operator('$last', $expression); } - /** - * Binds variables for use in the specified expression, and returns the - * result of the expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/let/ - * - * @param mixed|self $vars Assignment block for the variables accessible in the in expression. To assign a variable, specify a string for the variable name and assign a valid expression for the value. - * @param mixed|self $in the expression to evaluate - */ - public function let($vars, $in): self + public function lastN($expression, $n): static + { + return $this->operator('$lastN', [ + 'input' => $expression, + 'n' => $n, + ]); + } + + public function let($vars, $in): static { return $this->operator('$let', ['vars' => $vars, 'in' => $in]); } - /** - * Returns a value without parsing. Use for values that the aggregation - * pipeline may interpret as an expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/literal/ - * - * @param mixed|self $value - */ - public function literal($value): self + public function literal($value): static { return $this->operator('$literal', $value); } - /** - * Calculates the natural logarithm ln (i.e loge) of a number and returns - * the result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ - * - * @param mixed|self $number - */ - public function ln($number): self + public function ln($number): static { return $this->operator('$ln', $number); } - /** - * Calculates the log of a number in the specified base and returns the - * result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * The expression can be any valid expression as long as it resolves - * to a positive number greater than 1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ - * - * @param mixed|self $number - * @param mixed|self $base - */ - public function log($number, $base): self + public function log($number, $base): static { - return $this->operator('$log', [$number, $base]); + return $this->operator('$log', func_get_args()); } - /** - * Calculates the log base 10 of a number and returns the result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log10/ - * - * @param mixed|self $number - */ - public function log10($number): self + public function log10($number): static { return $this->operator('$log10', $number); } - /** - * Compares two values and returns: - * true when the first value is less than the second value. - * false when the first value is greater than or equivalent to the second - * value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/lt/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function lt($expression1, $expression2): self + public function lt($expression1, $expression2): static { - return $this->operator('$lt', [$expression1, $expression2]); + return $this->operator('$lt', func_get_args()); } - /** - * Compares two values and returns: - * true when the first value is less than or equivalent to the second value. - * false when the first value is greater than the second value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/lte/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function lte($expression1, $expression2): self + public function lte($expression1, $expression2): static { - return $this->operator('$lte', [$expression1, $expression2]); + return $this->operator('$lte', func_get_args()); } - /** - * Applies an expression to each item in an array and returns an array with - * the applied results. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/map/ - * - * @param mixed|self $input an expression that resolves to an array - * @param string $as The variable name for the items in the input array. The in expression accesses each item in the input array by this variable. - * @param mixed|self $in The expression to apply to each item in the input array. The expression accesses the item by its variable name. - */ - public function map($input, $as, $in): self + public function map($input, $as, $in): static { return $this->operator('$map', ['input' => $input, 'as' => $as, 'in' => $in]); } - /** - * Returns the highest value that results from applying an expression to - * each document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/max/ - * - * @param mixed|self $expression - */ - public function max($expression): self + public function max($expression, ...$expressions): static { - return $this->operator('$max', $expression); + return $this->accumulatorOperator('$max', ...func_get_args()); } - /** - * Returns the metadata associated with a document in a pipeline operations. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/meta/ - * - * @param mixed|self $metaDataKeyword - */ - public function meta($metaDataKeyword): self + public function maxN($expression, $n): static + { + return $this->operator('$maxN', [ + 'input' => $expression, + 'n' => $n, + ]); + } + + public function mergeObjects($expression, ...$expressions): static + { + return $this->accumulatorOperator('$mergeObjects', ...func_get_args()); + } + + public function meta($metaDataKeyword): static { return $this->operator('$meta', $metaDataKeyword); } - /** - * Returns the millisecond portion of a date as an integer between 0 and 999. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/millisecond/ - * - * @param mixed|self $expression - */ - public function millisecond($expression): self + public function millisecond($expression): static { return $this->operator('$millisecond', $expression); } - /** - * Returns the lowest value that results from applying an expression to each - * document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/min/ - * - * @param mixed|self $expression - */ - public function min($expression): self + public function min($expression, ...$expressions): static { - return $this->operator('$min', $expression); + return $this->accumulatorOperator('$min', ...func_get_args()); } - /** - * Returns the minute portion of a date as a number between 0 and 59. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/minute/ - * - * @param mixed|self $expression - */ - public function minute($expression): self + public function minN($expression, $n): static + { + return $this->operator('$minN', [ + 'input' => $expression, + 'n' => $n, + ]); + } + + public function minute($expression): static { return $this->operator('$minute', $expression); } - /** - * Divides one number by another and returns the remainder. The first - * argument is divided by the second argument. - * - * The arguments can be any valid expression as long as they resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/mod/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function mod($expression1, $expression2): self + public function mod($expression1, $expression2): static { - return $this->operator('$mod', [$expression1, $expression2]); + return $this->operator('$mod', func_get_args()); } - /** - * Returns the month of a date as a number between 1 and 12. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/month/ - * - * @param mixed|self $expression - */ - public function month($expression): self + public function month($expression): static { return $this->operator('$month', $expression); } - /** - * Multiplies numbers together and returns the result. - * - * The arguments can be any valid expression as long as they resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/multiply/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - * @param mixed|self ...$expressions Additional expressions - */ - public function multiply($expression1, $expression2, ...$expressions): self + public function multiply($expression1, $expression2, ...$expressions): static { return $this->operator('$multiply', func_get_args()); } - /** - * Compares two values and returns: - * true when the values are not equivalent. - * false when the values are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ne/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function ne($expression1, $expression2): self + public function ne($expression1, $expression2): static { - return $this->operator('$ne', [$expression1, $expression2]); + return $this->operator('$ne', func_get_args()); } - /** - * Evaluates a boolean and returns the opposite boolean value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/not/ - * - * @param mixed|self $expression - */ - public function not($expression): self + public function not($expression): static { return $this->operator('$not', $expression); } - /** - * Raises a number to the specified exponent and returns the result. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/pow/ - * - * @param mixed|self $number - * @param mixed|self $exponent - */ - public function pow($number, $exponent): self + public function pow($number, $exponent): static { - return $this->operator('$pow', [$number, $exponent]); + return $this->operator('$pow', func_get_args()); } - /** - * Returns an array of all values that result from applying an expression to - * each document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/push/ - * - * @param mixed|self $expression - */ - public function push($expression): self + public function push($expression): static { return $this->operator('$push', $expression); } - /** - * Returns an array whose elements are a generated sequence of numbers. - * - * $range generates the sequence from the specified starting number by successively incrementing the starting number by the specified step value up to but not including the end point. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/range/ - * - * @param mixed|self $start An integer that specifies the start of the sequence. Can be any valid expression that resolves to an integer. - * @param mixed|self $end An integer that specifies the exclusive upper limit of the sequence. Can be any valid expression that resolves to an integer. - * @param mixed|self $step Optional. An integer that specifies the increment value. Can be any valid expression that resolves to a non-zero integer. Defaults to 1. - */ - public function range($start, $end, $step = 1): self + public function rand(): static { - return $this->operator('$range', [$start, $end, $step]); + return $this->operator('$rand', []); } - /** - * Applies an expression to each element in an array and combines them into - * a single value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/ - * - * @param mixed|self $input can be any valid expression that resolves to an array - * @param mixed|self $initialValue the initial cumulative value set before in is applied to the first element of the input array - * @param mixed|self $in A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. - */ - public function reduce($input, $initialValue, $in): self + public function range($start, $end, $step = null): static + { + return $this->operator('$range', func_get_args()); + } + + public function reduce($input, $initialValue, $in): static { return $this->operator('$reduce', ['input' => $input, 'initialValue' => $initialValue, 'in' => $in]); } - /** - * Accepts an array expression as an argument and returns an array with the - * elements in reverse order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/reverseArray/ - * - * @param mixed|self $expression - */ - public function reverseArray($expression): self + public function reverseArray($expression): static { return $this->operator('$reverseArray', $expression); } - /** - * Returns the second portion of a date as a number between 0 and 59, but - * can be 60 to account for leap seconds. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/second/ - * - * @param mixed|self $expression - */ - public function second($expression): self + public function sampleRate(float $rate): static + { + return $this->operator('$sampleRate', $rate); + } + + public function second($expression): static { return $this->operator('$second', $expression); } - /** - * Takes two sets and returns an array containing the elements that only - * exist in the first set. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setDifference/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function setDifference($expression1, $expression2): self + public function setDifference($expression1, $expression2): static { - return $this->operator('$setDifference', [$expression1, $expression2]); + return $this->operator('$setDifference', func_get_args()); } - /** - * Compares two or more arrays and returns true if they have the same - * distinct elements and false otherwise. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setEquals/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - * @param mixed|self ...$expressions Additional sets - */ - public function setEquals($expression1, $expression2, ...$expressions): self + public function setEquals($expression1, $expression2, ...$expressions): static { return $this->operator('$setEquals', func_get_args()); } - /** - * Takes two or more arrays and returns an array that contains the elements - * that appear in every input array. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setIntersection/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - * @param mixed|self ...$expressions Additional sets - */ - public function setIntersection($expression1, $expression2, ...$expressions): self + public function setField($field, $input, $value): static + { + return $this->operator('$setField', ['field' => $field, 'input' => $input, 'value' => $value]); + } + + public function setIntersection($expression1, $expression2, ...$expressions): static { return $this->operator('$setIntersection', func_get_args()); } - /** - * Takes two arrays and returns true when the first array is a subset of the - * second, including when the first array equals the second array, and false otherwise. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setIsSubset/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function setIsSubset($expression1, $expression2): self + public function setIsSubset($expression1, $expression2): static { - return $this->operator('$setIsSubset', [$expression1, $expression2]); + return $this->operator('$setIsSubset', func_get_args()); } - /** - * Takes two or more arrays and returns an array containing the elements - * that appear in any input array. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setUnion/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - * @param mixed|self ...$expressions Additional sets - */ - public function setUnion($expression1, $expression2, ...$expressions): self + public function setUnion($expression1, $expression2, ...$expressions): static { return $this->operator('$setUnion', func_get_args()); } - /** - * Counts and returns the total the number of items in an array. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/size/ - * - * @param mixed|self $expression - */ - public function size($expression): self + public function size($expression): static { return $this->operator('$size', $expression); } - /** - * Returns a subset of an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/slice/ - * - * @param mixed|self $array - * @param mixed|self $n - * @param mixed|self|null $position - */ - public function slice($array, $n, $position = null): self + public function slice($array, $n, $position = null): static { - if ($position === null) { - return $this->operator('$slice', [$array, $n]); + // With two args provided, the order of parameters is , . + // With three args provided, the order of parameters is , + // , . + if ($position !== null) { + $args = [$array, $position, $n]; + } else { + $args = [$array, $n]; } - return $this->operator('$slice', [$array, $position, $n]); + return $this->operator('$slice', $args); } - /** - * Divides a string into an array of substrings based on a delimiter. - * - * $split removes the delimiter and returns the resulting substrings as - * elements of an array. If the delimiter is not found in the string, $split - * returns the original string as the only element of an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/split/ - * - * @param mixed|self $string The string to be split. Can be any valid expression as long as it resolves to a string. - * @param mixed|self $delimiter The delimiter to use when splitting the string expression. Can be any valid expression as long as it resolves to a string. - */ - public function split($string, $delimiter): self + public function sortArray($input, $sortBy): static { - return $this->operator('$split', [$string, $delimiter]); + return $this->operator('$sortArray', [ + 'input' => $input, + 'sortBy' => $sortBy, + ]); } - /** - * Calculates the square root of a positive number and returns the result as - * a double. - * - * The argument can be any valid expression as long as it resolves to a - * non-negative number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sqrt/ - * - * @param mixed|self $expression - */ - public function sqrt($expression): self + public function split($string, $delimiter): static { - return $this->operator('$sqrt', $expression); + return $this->operator('$split', func_get_args()); } - /** - * Calculates the population standard deviation of the input values. - * - * The arguments can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevPop/ - * - * @param mixed|self $expression1 - * @param mixed|self ...$expressions Additional samples - */ - public function stdDevPop($expression1, ...$expressions): self + public function sqrt($expression): static { - $expression = empty($expressions) ? $expression1 : func_get_args(); - - return $this->operator('$stdDevPop', $expression); + return $this->operator('$sqrt', $expression); } - /** - * Calculates the sample standard deviation of the input values. - * - * The arguments can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevSamp/ - * - * @param mixed|self $expression1 - * @param mixed|self ...$expressions Additional samples - */ - public function stdDevSamp($expression1, ...$expressions): self + public function stdDevPop($expression, ...$expressions): static { - $expression = empty($expressions) ? $expression1 : func_get_args(); + return $this->accumulatorOperator('$stdDevPop', ...func_get_args()); + } - return $this->operator('$stdDevSamp', $expression); + public function stdDevSamp($expression, ...$expressions): static + { + return $this->accumulatorOperator('$stdDevSamp', ...func_get_args()); } - /** - * Performs case-insensitive comparison of two strings. Returns - * 1 if first string is “greater than” the second string. - * 0 if the two strings are equal. - * -1 if the first string is “less than” the second string. - * - * The arguments can be any valid expression as long as they resolve to strings. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strcasecmp/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function strcasecmp($expression1, $expression2): self + public function strcasecmp($expression1, $expression2): static { - return $this->operator('$strcasecmp', [$expression1, $expression2]); + return $this->operator('$strcasecmp', func_get_args()); } - /** - * Returns the number of UTF-8 encoded bytes in the specified string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strLenBytes/ - * - * @param mixed|self $string - */ - public function strLenBytes($string): self + public function strLenBytes($string): static { return $this->operator('$strLenBytes', $string); } - /** - * Returns the number of UTF-8 code points in the specified string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strLenCP/ - * - * @param mixed|self $string - */ - public function strLenCP($string): self + public function strLenCP($string): static { return $this->operator('$strLenCP', $string); } - /** - * Returns a substring of a string, starting at a specified index position - * and including the specified number of characters. The index is zero-based. - * - * The arguments can be any valid expression as long as long as the first argument resolves to a string, and the second and third arguments resolve to integers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substr/ - * - * @param mixed|self $string - * @param mixed|self $start - * @param mixed|self $length - */ - public function substr($string, $start, $length): self + public function substr($string, $start, $length): static { - return $this->operator('$substr', [$string, $start, $length]); + return $this->operator('$substr', func_get_args()); } - /** - * Returns the substring of a string. - * - * The substring starts with the character at the specified UTF-8 byte index - * (zero-based) in the string and continues for the number of bytes - * specified. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substrBytes/ - * - * @param mixed|self $string The string from which the substring will be extracted. Can be any valid expression as long as it resolves to a string. - * @param mixed|self $start Indicates the starting point of the substring. Can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer. - * @param mixed|self $count can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer - */ - public function substrBytes($string, $start, $count): self + public function substrBytes($string, $start, $count): static { - return $this->operator('$substrBytes', [$string, $start, $count]); + return $this->operator('$substrBytes', func_get_args()); } - /** - * Returns the substring of a string. - * - * The substring starts with the character at the specified UTF-8 code point - * (CP) index (zero-based) in the string for the number of code points - * specified. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substrBytes/ - * - * @param mixed|self $string The string from which the substring will be extracted. Can be any valid expression as long as it resolves to a string. - * @param mixed|self $start Indicates the starting point of the substring. Can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer. - * @param mixed|self $count can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer - */ - public function substrCP($string, $start, $count): self + public function substrCP($string, $start, $count): static { - return $this->operator('$substrCP', [$string, $start, $count]); + return $this->operator('$substrCP', func_get_args()); } - /** - * Subtracts two numbers to return the difference. The second argument is - * subtracted from the first argument. - * - * The arguments can be any valid expression as long as they resolve to numbers and/or dates. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/subtract/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function subtract($expression1, $expression2): self + public function subtract($expression1, $expression2): static { - return $this->operator('$subtract', [$expression1, $expression2]); + return $this->operator('$subtract', func_get_args()); } - /** - * Calculates and returns the sum of all the numeric values that result from - * applying a specified expression to each document in a group of documents - * that share the same group by key. Ignores nun-numeric values. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sum/ - * - * @param mixed|self $expression - */ - public function sum($expression): self + public function sum($expression, ...$expressions): static { - return $this->operator('$sum', $expression); + return $this->accumulatorOperator('$sum', ...func_get_args()); } - /** - * Converts value to a boolean. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toBool/ - * - * @param mixed|self $expression - */ - public function toBool($expression): self + public function toBool($expression): static { return $this->operator('$toBool', $expression); } - /** - * Converts value to a Date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDate/ - * - * @param mixed|self $expression - */ - public function toDate($expression): self + public function toDate($expression): static { return $this->operator('$toDate', $expression); } - /** - * Converts value to a Decimal128. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDecimal/ - * - * @param mixed|self $expression - */ - public function toDecimal($expression): self + public function toDecimal($expression): static { return $this->operator('$toDecimal', $expression); } - /** - * Converts value to a double. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDouble/ - * - * @param mixed|self $expression - */ - public function toDouble($expression): self + public function toDouble($expression): static { return $this->operator('$toDouble', $expression); } - /** - * Converts value to an integer. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toInt/ - * - * @param mixed|self $expression - */ - public function toInt($expression): self + public function toInt($expression): static { return $this->operator('$toInt', $expression); } - /** - * Converts value to a long. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toLong/ - * - * @param mixed|self $expression - */ - public function toLong($expression): self + public function toLong($expression): static { return $this->operator('$toLong', $expression); } - /** - * Converts a string to lowercase, returning the result. - * - * The argument can be any expression as long as it resolves to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toLower/ - * - * @param mixed|self $expression - */ - public function toLower($expression): self + public function toLower($expression): static { return $this->operator('$toLower', $expression); } - /** - * Converts value to an ObjectId. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toObjectId/ - * - * @param mixed|self $expression - */ - public function toObjectId($expression): self + public function toObjectId($expression): static { return $this->operator('$toObjectId', $expression); } - /** - * Converts value to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toString/ - * - * @param mixed|self $expression - */ - public function toString($expression): self + public function top($output, $sortBy): static + { + return $this->operator('$top', ['output' => $output, 'sortBy' => $sortBy]); + } + + public function topN($output, $sortBy, $n): static + { + return $this->operator('$topN', ['output' => $output, 'sortBy' => $sortBy, 'n' => $n]); + } + + public function toString($expression): static { return $this->operator('$toString', $expression); } - /** - * Converts a string to uppercase, returning the result. - * - * The argument can be any expression as long as it resolves to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toUpper/ - * - * @param mixed|self $expression - */ - public function toUpper($expression): self + public function toUpper($expression): static { return $this->operator('$toUpper', $expression); } - /** - * Truncates a number to its integer. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/trunc/ - * - * @param mixed|self $number - */ - public function trunc($number): self + public function trunc($number): static { return $this->operator('$trunc', $number); } - /** - * Returns a string that specifies the BSON type of the argument. - * - * The argument can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/type/ - * - * @param mixed|self $expression - */ - public function type($expression): self + public function tsIncrement($expression): static + { + return $this->operator('$tsIncrement', $expression); + } + + public function tsSecond($expression): static + { + return $this->operator('$tsSecond', $expression); + } + + public function type($expression): static { return $this->operator('$type', $expression); } - /** - * Returns the week of the year for a date as a number between 0 and 53. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/week/ - * - * @param mixed|self $expression - */ - public function week($expression): self + public function week($expression): static { return $this->operator('$week', $expression); } - /** - * Returns the year portion of a date. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/year/ - * - * @param mixed|self $expression - */ - public function year($expression): self + public function year($expression): static { return $this->operator('$year', $expression); } - /** - * Transposes an array of input arrays so that the first element of the - * output array would be an array containing, the first element of the first - * input array, the first element of the second input array, etc. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/zip/ - * - * @param mixed|self $inputs An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. - * @param bool|null $useLongestLength a boolean which specifies whether the length of the longest array determines the number of arrays in the output array - * @param mixed|self|null $defaults An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. - */ - public function zip($inputs, ?bool $useLongestLength = null, $defaults = null): self + public function zip($inputs, ?bool $useLongestLength = null, $defaults = null): static { $args = ['inputs' => $inputs]; if ($useLongestLength !== null) { @@ -1639,18 +1005,40 @@ public function zip($inputs, ?bool $useLongestLength = null, $defaults = null): } /** + * Wrapper for accumulator operators that exist in forms with one and multiple arguments + * + * @see Expr::operator() + * + * @param mixed|self ...$expressions + */ + private function accumulatorOperator(string $operator, ...$expressions): static + { + if (count($expressions) === 1) { + return $this->operator($operator, $expressions[0]); + } + + return $this->operator($operator, $expressions); + } + + /** + * Prepares an argument for an operator. It follows these ruls: + * - If the argument is a string starting with a $, it is considered a field name and is transformed according to mapping information. + * - If the argument is an array, it is recursively prepared. + * - If the argument is an Expr instance, its expression is returned. + * - Otherwise, the argument is converted to a MongoDB type according to the ODM type information. + * * @param mixed|self $expression * * @return mixed */ - private function ensureArray($expression) + private function prepareArgument($expression) { if (is_string($expression) && substr($expression, 0, 1) === '$') { return '$' . $this->getDocumentPersister()->prepareFieldName(substr($expression, 1)); } if (is_array($expression)) { - return array_map([$this, 'ensureArray'], $expression); + return array_map([$this, 'prepareArgument'], $expression); } if ($expression instanceof self) { @@ -1672,29 +1060,22 @@ private function getDocumentPersister(): DocumentPersister * If there is a current field, the operator will be set on it; otherwise, * the operator is set at the top level of the query. * - * @param mixed[]|self $expression + * @param mixed|mixed[]|self $expression */ - private function operator(string $operator, $expression): self + private function operator(string $operator, $expression): static { if ($this->currentField) { - $this->expr[$this->currentField][$operator] = $this->ensureArray($expression); + $this->expr[$this->currentField][$operator] = $this->prepareArgument($expression); } else { - $this->expr[$operator] = $this->ensureArray($expression); + $this->expr[$operator] = $this->prepareArgument($expression); } return $this; } - /** - * Ensure that a current field has been set. - * - * @throws LogicException if a current field has not been set. - */ - private function requiresCurrentField(?string $method = null): void + public function or($expression, ...$expressions): static { - if (! $this->currentField) { - throw new LogicException(($method ?: 'This method') . ' requires you set a current field using field().'); - } + return $this->operator('$or', func_get_args()); } /** @throws BadMethodCallException if there is no current switch operator. */ @@ -1711,30 +1092,14 @@ private function requiresSwitchStatement(?string $method = null): void } } - /** - * Evaluates a series of case expressions. When it finds an expression which - * evaluates to true, $switch executes a specified expression and breaks out - * of the control flow. - * - * To add statements, use the {@link case()}, {@link then()} and - * {@link default()} methods. - */ - public function switch(): self + public function switch(): static { $this->operator('$switch', []); return $this; } - /** - * Adds a case statement for the current branch of the $switch operator. - * - * Requires {@link case()} to be called first. The argument can be any valid - * expression. - * - * @param mixed|self $expression - */ - public function then($expression): self + public function then($expression): static { if (! is_array($this->switchBranch)) { throw new BadMethodCallException(static::class . '::then requires a valid case statement (call case() first).'); @@ -1743,9 +1108,9 @@ public function then($expression): self $this->switchBranch['then'] = $expression; if ($this->currentField) { - $this->expr[$this->currentField]['$switch']['branches'][] = $this->ensureArray($this->switchBranch); + $this->expr[$this->currentField]['$switch']['branches'][] = $this->prepareArgument($this->switchBranch); } else { - $this->expr['$switch']['branches'][] = $this->ensureArray($this->switchBranch); + $this->expr['$switch']['branches'][] = $this->prepareArgument($this->switchBranch); } $this->switchBranch = null; @@ -1753,310 +1118,211 @@ public function then($expression): self return $this; } - /** - * Converts an array into a single document. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/ - * - * @param mixed|self $array - */ - public function arrayToObject($array): self + public function arrayToObject($array): static { return $this->operator('$arrayToObject', $array); } - /** - * Converts a document to an array. The return array contains an element for each field/value pair - * in the original document. Each element in the return array is a document that contains - * two fields k and v:. - * The k field contains the field name in the original document. - * The v field contains the value of the field in the original document. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/objectToArray/ - * - * @param mixed|self $object - */ - public function objectToArray($object): self + public function objectToArray($object): static { return $this->operator('$objectToArray', $object); } - /** - * Rounds a number to a whole integer or to a specified decimal place. - * - * The argument can be any valid expression as long as it resolves - * to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/round/ - * - * @param mixed|self $number - * @param mixed|self|null $place - */ - public function round($number, $place = null): self + public function regexFind($input, $regex, $options = null): static { - return $this->operator('$round', [$number, $place]); + return $this->operator( + '$regexFind', + $this->filterOptionalNullArguments( + [ + 'input' => $input, + 'regex' => $regex, + 'options' => $options, + ], + ['options'], + ), + ); } - /** - * Removes whitespace characters, including null, or the specified characters from - * the beginning and end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/trim/ - * - * @param mixed|self $input - * @param mixed|self $chars - */ - public function trim($input, $chars = null): self + public function regexFindAll($input, $regex, $options = null): static { - return $this->operator('$trim', [$input, $chars]); + return $this->operator( + '$regexFindAll', + $this->filterOptionalNullArguments( + [ + 'input' => $input, + 'regex' => $regex, + 'options' => $options, + ], + ['options'], + ), + ); } - /** - * Removes whitespace characters, including null, or the specified characters from - * the beginning and end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ltrim/ - * - * @param mixed|self $input - * @param mixed|self $chars - */ - public function ltrim($input, $chars = null): self + public function regexMatch($input, $regex, $options = null): static { - return $this->operator('$ltrim', [$input, $chars]); + return $this->operator( + '$regexMatch', + $this->filterOptionalNullArguments( + [ + 'input' => $input, + 'regex' => $regex, + 'options' => $options, + ], + ['options'], + ), + ); } - /** - * Removes whitespace characters, including null, or the specified characters from the end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/rtrim/ - * - * @param mixed|self $input - * @param mixed|self $chars - */ - public function rtrim($input, $chars = null): self + public function replaceAll($input, $find, $replacement): static { - return $this->operator('$rtrim', [$input, $chars]); + return $this->operator('$replaceAll', [ + 'input' => $input, + 'find' => $find, + 'replacement' => $replacement, + ]); } - /** - * Returns the sine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sin/ - * - * @param mixed|self $expression - */ - public function sin($expression): self + public function replaceOne($input, $find, $replacement): static + { + return $this->operator('$replaceOne', [ + 'input' => $input, + 'find' => $find, + 'replacement' => $replacement, + ]); + } + + public function round($number, $place = null): static + { + return $this->operator('$round', func_get_args()); + } + + public function trim($input, $chars = null): static + { + return $this->operator('$trim', func_get_args()); + } + + public function ltrim($input, $chars = null): static + { + return $this->operator('$ltrim', func_get_args()); + } + + public function rtrim($input, $chars = null): static + { + return $this->operator('$rtrim', func_get_args()); + } + + public function sin($expression): static { return $this->operator('$sin', $expression); } - /** - * Returns the cosine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cos/ - * - * @param mixed|self $expression - */ - public function cos($expression): self + public function cos($expression): static { return $this->operator('$cos', $expression); } - /** - * Returns the tangent of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/tan/ - * - * @param mixed|self $expression - */ - public function tan($expression): self + public function tan($expression): static { return $this->operator('$tan', $expression); } - /** - * Returns the inverse sin (arc sine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/asin/ - * - * @param mixed|self $expression - */ - public function asin($expression): self + public function asin($expression): static { return $this->operator('$asin', $expression); } - /** - * Returns the inverse cosine (arc cosine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/acos/ - * - * @param mixed|self $expression - */ - public function acos($expression): self + public function acos($expression): static { return $this->operator('$acos', $expression); } - /** - * Returns the inverse tangent (arc tangent) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atan/ - * - * @param mixed|self $expression - */ - public function atan($expression): self + public function atan($expression): static { return $this->operator('$atan', $expression); } - /** - * Returns the inverse tangent (arc tangent) of y / x in radians, where y and x are the first and second values passed to the expression respectively. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atan2/ - * - * @param mixed|self $expression1 - * @param mixed|self $expression2 - */ - public function atan2($expression1, $expression2): self + public function atan2($expression1, $expression2): static { - return $this->operator('$atan2', [$expression1, $expression2]); + return $this->operator('$atan2', func_get_args()); } - /** - * Returns the inverse hyperbolic sine (hyperbolic arc sine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/asinh/ - * - * @param mixed|self $expression - */ - public function asinh($expression): self + public function asinh($expression): static { return $this->operator('$asinh', $expression); } - /** - * Returns the inverse hyperbolic cosine (hyperbolic arc cosine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/acosh/ - * - * @param mixed|self $expression - */ - public function acosh($expression): self + public function acosh($expression): static { return $this->operator('$acosh', $expression); } - /** - * Returns the inverse hyperbolic tangent (hyperbolic arc tangent) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atanh/ - * - * @param mixed|self $expression - */ - public function atanh($expression): self + public function atanh($expression): static { return $this->operator('$atanh', $expression); } - /** - * Returns the hyperbolic sine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sinh/ - * - * @param mixed|self $expression - */ - public function sinh($expression): self + public function sinh($expression): static { return $this->operator('$sinh', $expression); } - /** - * Returns the hyperbolic cosine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cosh/ - * - * @param mixed|self $expression - */ - public function cosh($expression): self + public function cosh($expression): static { return $this->operator('$cosh', $expression); } - /** - * Returns the hyperbolic tangent of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/tanh/ - * - * @param mixed|self $expression - */ - public function tanh($expression): self + public function tanh($expression): static { return $this->operator('$tanh', $expression); } - /** - * Converts a value from degrees to radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/degreesToRadians/ - * - * @param mixed|self $expression - */ - public function degreesToRadians($expression): self + public function degreesToRadians($expression): static { return $this->operator('$degreesToRadians', $expression); } - /** - * Converts a value from radians to degrees. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/radiansToDegrees/ - * - * @param mixed|self $expression - */ - public function radiansToDegrees($expression): self + public function radiansToDegrees($expression): static { return $this->operator('$radiansToDegrees', $expression); } - /** - * Converts a value to a specified type. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/convert/ - * - * @param mixed|self $input - * @param mixed|self $to - * @param mixed|self|null $onError - * @param mixed|self|null $onNull - */ - public function convert($input, $to, $onError = null, $onNull = null): self + public function convert($input, $to, $onError = null, $onNull = null): static { - $params = [ - 'input' => $input, - 'to' => $to, - ]; - - if ($onError !== null) { - $params['onError'] = $onError; - } - - if ($onNull !== null) { - $params['onNull'] = $onNull; - } + return $this->operator( + '$convert', + $this->filterOptionalNullArguments( + [ + 'input' => $input, + 'to' => $to, + 'onError' => $onError, + 'onNull' => $onNull, + ], + ['onError', 'onNull'], + ), + ); + } - return $this->operator('$convert', $params); + public function isNumber($expression): static + { + return $this->operator('$isNumber', $expression); } /** - * Returns boolean true if the specified expression resolves to an integer, decimal, double, or long. - * Returns boolean false if the expression resolves to any other BSON type, null, or a missing field. + * @param array $args + * @param list $optionalArgNames * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isNumber/ - * - * @param mixed|self $expression + * @return array */ - public function isNumber($expression): self + private function filterOptionalNullArguments(array $args, array $optionalArgNames): array { - return $this->operator('$isNumber', $expression); + return array_filter( + $args, + /** + * @param mixed $value + * @param array-key $key + */ + static fn ($value, $key): bool => $value !== null || ! in_array($key, $optionalArgNames), + ARRAY_FILTER_USE_BOTH, + ); } } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/AccumulatorOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/AccumulatorOperators.php new file mode 100644 index 0000000000..c9c331afe4 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/AccumulatorOperators.php @@ -0,0 +1,89 @@ + argument can be any valid expression as long as it resolves + * to a number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/abs/ + * + * @param mixed|Expr $number + */ + public function abs($number): static; + + /** + * Adds numbers together or adds numbers and a date. If one of the arguments + * is a date, $add treats the other arguments as milliseconds to add to the + * date. + * + * The arguments can be any valid expression as long as they resolve to + * either all numbers or to numbers and a date. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/add/ + * + * @param mixed|Expr $expression1 + * @param mixed|Expr $expression2 + * @param mixed|Expr ...$expressions Additional expressions + */ + public function add($expression1, $expression2, ...$expressions): static; + + /** + * Returns the smallest integer greater than or equal to the specified + * number. + * + * The expression can be any valid expression as long as it + * resolves to a number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ceil/ + * + * @param mixed|Expr $number + */ + public function ceil($number): static; + + /** + * Divides one number by another and returns the result. The first argument + * is divided by the second argument. + * + * The arguments can be any valid expression as long as the resolve to numbers. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/divide/ + * + * @param mixed|Expr $expression1 + * @param mixed|Expr $expression2 + */ + public function divide($expression1, $expression2): static; + + /** + * Raises Euler’s number to the specified exponent and returns the result. + * + * The expression can be any valid expression as long as it + * resolves to a number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/exp/ + * + * @param mixed|Expr $exponent + */ + public function exp($exponent): static; + + /** + * Returns the largest integer less than or equal to the specified number. + * + * The expression can be any valid expression as long as it + * resolves to a number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/floor/ + * + * @param mixed|Expr $number + */ + public function floor($number): static; + + /** + * Calculates the natural logarithm ln (i.e loge) of a number and returns + * the result as a double. + * + * The expression can be any valid expression as long as it + * resolves to a non-negative number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ + * + * @param mixed|Expr $number + */ + public function ln($number): static; + + /** + * Calculates the log of a number in the specified base and returns the + * result as a double. + * + * The expression can be any valid expression as long as it + * resolves to a non-negative number. + * The expression can be any valid expression as long as it resolves + * to a positive number greater than 1. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ + * + * @param mixed|Expr $number + * @param mixed|Expr $base + */ + public function log($number, $base): static; + + /** + * Calculates the log base 10 of a number and returns the result as a double. + * + * The expression can be any valid expression as long as it + * resolves to a non-negative number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log10/ + * + * @param mixed|Expr $number + */ + public function log10($number): static; + + /** + * Divides one number by another and returns the remainder. The first + * argument is divided by the second argument. + * + * The arguments can be any valid expression as long as they resolve to + * numbers. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/mod/ + * + * @param mixed|Expr $expression1 + * @param mixed|Expr $expression2 + */ + public function mod($expression1, $expression2): static; + + /** + * Multiplies numbers together and returns the result. + * + * The arguments can be any valid expression as long as they resolve to + * numbers. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/multiply/ + * + * @param mixed|Expr $expression1 + * @param mixed|Expr $expression2 + * @param mixed|Expr ...$expressions Additional expressions + */ + public function multiply($expression1, $expression2, ...$expressions): static; + + /** + * Raises a number to the specified exponent and returns the result. + * + * The expression can be any valid expression as long as it + * resolves to a non-negative number. + * The expression can be any valid expression as long as it + * resolves to a number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/pow/ + * + * @param mixed|Expr $number + * @param mixed|Expr $exponent + */ + public function pow($number, $exponent): static; + + /** + * Rounds a number to a whole integer or to a specified decimal place. + * + * The argument can be any valid expression as long as it resolves + * to a number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/round/ + * + * @param mixed|Expr $number + * @param mixed|Expr|null $place + */ + public function round($number, $place = null): static; + + /** + * Calculates the square root of a positive number and returns the result as + * a double. + * + * The argument can be any valid expression as long as it resolves to a + * non-negative number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sqrt/ + * + * @param mixed|Expr $expression + */ + public function sqrt($expression): static; + + /** + * Subtracts two numbers to return the difference. The second argument is + * subtracted from the first argument. + * + * The arguments can be any valid expression as long as they resolve to numbers and/or dates. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/subtract/ + * + * @param mixed|Expr $expression1 + * @param mixed|Expr $expression2 + */ + public function subtract($expression1, $expression2): static; + + /** + * Truncates a number to its integer. + * + * The expression can be any valid expression as long as it + * resolves to a number. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/trunc/ + * + * @param mixed|Expr $number + */ + public function trunc($number): static; +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/ArrayOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/ArrayOperators.php new file mode 100644 index 0000000000..9a054ecb7a --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/ArrayOperators.php @@ -0,0 +1,278 @@ + expression can be any valid expression as long as it resolves + * to an array. + * The expression can be any valid expression as long as it resolves + * to an integer. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayElemAt/ + * + * @param mixed|Expr $array + * @param mixed|Expr $index + */ + public function arrayElemAt($array, $index): static; + + /** + * Converts an array into a single document. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/ + * + * @param mixed|Expr $array + */ + public function arrayToObject($array): static; + + /** + * Concatenates arrays to return the concatenated array. + * + * The expressions can be any valid expression as long as they + * resolve to an array. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/concatArrays/ + * + * @param mixed|Expr $array1 + * @param mixed|Expr $array2 + * @param mixed|Expr ...$arrays Additional expressions + */ + public function concatArrays($array1, $array2, ...$arrays): static; + + /** + * Selects a subset of the array to return based on the specified condition. + * + * Returns an array with only those elements that match the condition. The + * returned elements are in the original order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/filter/ + * + * @param mixed|Expr $input + * @param mixed|Expr $as + * @param mixed|Expr $cond + */ + public function filter($input, $as, $cond): static; + + /** + * Returns the first array element. Distinct from the $first group + * accumulator. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/first-array-element/ + * + * @param mixed|Expr $expression + */ + public function first($expression): static; + + /** + * Returns a specified number of elements from the beginning of an array. + * Distinct from the $firstN group accumulator. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN-array-element/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $n + */ + public function firstN($expression, $n): static; + + /** + * Returns a boolean indicating whether a specified value is in an array. + * + * Unlike the $in query operator, the aggregation $in operator does not + * support matching by regular expressions. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/in/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $arrayExpression + */ + public function in($expression, $arrayExpression): static; + + /** + * Searches an array for an occurrence of a specified value and returns the + * array index (zero-based) of the first occurrence. If the value is not + * found, returns -1. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfArray/ + * + * @param mixed|Expr $arrayExpression can be any valid expression as long as it resolves to an array + * @param mixed|Expr $searchExpression can be any valid expression + * @param mixed|Expr $start Optional. An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * @param mixed|Expr $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. + */ + public function indexOfArray($arrayExpression, $searchExpression, $start = null, $end = null): static; + + /** + * Determines if the operand is an array. Returns a boolean. + * + * The can be any valid expression. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isArray/ + * + * @param mixed|Expr $expression + */ + public function isArray($expression): static; + + /** + * Returns the last array element. Distinct from the $last group accumulator. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/last-array-element/ + * + * @param mixed|Expr $expression + */ + public function last($expression): static; + + /** + * Returns a specified number of elements from the end of an array. Distinct + * from the $lastN accumulator. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/lastN-array-element/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $n + */ + public function lastN($expression, $n): static; + + /** + * Applies an expression to each item in an array and returns an array with + * the applied results. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/map/ + * + * @param mixed|Expr $input an expression that resolves to an array + * @param string $as The variable name for the items in the input array. The in expression accesses each item in the input array by this variable. + * @param mixed|Expr $in The expression to apply to each item in the input array. The expression accesses the item by its variable name. + */ + public function map($input, $as, $in): static; + + /** + * Returns the n largest values in an array. Distinct from the $maxN group + * accumulator. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/maxN/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $n + */ + public function maxN($expression, $n): static; + + /** + * Returns the n smallest values in an array. Distinct from the $minN group + * accumulator. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/minN/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $n + */ + public function minN($expression, $n): static; + + /** + * Converts a document to an array. The return array contains an element for + * each field/value pair in the original document. Each element in the + * return array is a document that contains two fields k and v:. + * The k field contains the field name in the original document. + * The v field contains the value of the field in the original document. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/objectToArray/ + * + * @param mixed|Expr $object + */ + public function objectToArray($object): static; + + /** + * Returns an array whose elements are a generated sequence of numbers. + * + * $range generates the sequence from the specified starting number by + * successively incrementing the starting number by the specified step value + * up to but not including the end point. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/range/ + * + * @param mixed|Expr $start An integer that specifies the start of the sequence. Can be any valid expression that resolves to an integer. + * @param mixed|Expr $end An integer that specifies the exclusive upper limit of the sequence. Can be any valid expression that resolves to an integer. + * @param mixed|Expr $step Optional. An integer that specifies the increment value. Can be any valid expression that resolves to a non-zero integer. Defaults to 1. + */ + public function range($start, $end, $step = null): static; + + /** + * Applies an expression to each element in an array and combines them into + * a single value. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/ + * + * @param mixed|Expr $input can be any valid expression that resolves to an array + * @param mixed|Expr $initialValue the initial cumulative value set before in is applied to the first element of the input array + * @param mixed|Expr $in A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. + */ + public function reduce($input, $initialValue, $in): static; + + /** + * Accepts an array expression as an argument and returns an array with the + * elements in reverse order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/reverseArray/ + * + * @param mixed|Expr $expression + */ + public function reverseArray($expression): static; + + /** + * Counts and returns the total the number of items in an array. + * + * The argument can be any expression as long as it resolves to an array. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/size/ + * + * @param mixed|Expr $expression + */ + public function size($expression): static; + + /** + * Returns a subset of an array. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/slice/ + * + * @param mixed|Expr $array + * @param mixed|Expr $n + * @param mixed|Expr|null $position + */ + public function slice($array, $n, $position = null): static; + + /** + * Sorts an array based on its elements. The sort order is user specified. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sortArray/ + * + * @param mixed|Expr $input + * @param array $sortBy + */ + public function sortArray($input, $sortBy): static; + + /** + * Transposes an array of input arrays so that the first element of the + * output array would be an array containing, the first element of the first + * input array, the first element of the second input array, etc. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/zip/ + * + * @param mixed|Expr $inputs An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. + * @param bool|null $useLongestLength a boolean which specifies whether the length of the longest array determines the number of arrays in the output array + * @param mixed|Expr|null $defaults An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. + */ + public function zip($inputs, ?bool $useLongestLength = null, $defaults = null): static; +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/BooleanOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/BooleanOperators.php new file mode 100644 index 0000000000..862be5de09 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/BooleanOperators.php @@ -0,0 +1,47 @@ +|Expr $expression + * @param array|Expr ...$expressions + */ + public function and($expression, ...$expressions): static; + + /** + * Evaluates a boolean and returns the opposite boolean value. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/not/ + * + * @param mixed|Expr $expression + */ + public function not($expression): static; + + /** + * Adds one or more $or clause to the current expression. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/or/ + * + * @param array|Expr $expression + * @param array|Expr ...$expressions + */ + public function or($expression, ...$expressions): static; +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/ComparisonOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/ComparisonOperators.php new file mode 100644 index 0000000000..e5cd53c9d9 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/ComparisonOperators.php @@ -0,0 +1,104 @@ + argument can be any valid expression as long as it resolves - * to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/abs/ - * - * @param mixed|Expr $number - */ - public function abs($number): self; - - /** - * Returns the inverse cosine (arc cosine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/acos/ - * - * @param mixed|Expr $expression - */ - public function acos($expression): self; - - /** - * Returns the inverse hyperbolic cosine (hyperbolic arc cosine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/acosh/ - * - * @param mixed|Expr $expression - */ - public function acosh($expression): self; - - /** - * Adds numbers together or adds numbers and a date. If one of the arguments - * is a date, $add treats the other arguments as milliseconds to add to the - * date. - * - * The arguments can be any valid expression as long as they resolve to - * either all numbers or to numbers and a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/add/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function add($expression1, $expression2, ...$expressions): self; - - /** - * Adds one or more $and clauses to the current expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/and/ - * - * @param array|Expr $expression - * @param array|Expr ...$expressions - */ - public function addAnd($expression, ...$expressions): self; - - /** - * Adds one or more $or clause to the current expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/or/ - * - * @param array|Expr $expression - * @param array|Expr ...$expressions - */ - public function addOr($expression, ...$expressions): self; - - /** - * Evaluates an array as a set and returns true if no element in the array - * is false. Otherwise, returns false. An empty array returns true. - * - * The expression must resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/allElementsTrue/ - * - * @param mixed|Expr $expression - */ - public function allElementsTrue($expression): self; - - /** - * Evaluates an array as a set and returns true if any of the elements are - * true and false otherwise. An empty array returns false. - * - * The expression must resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/anyElementTrue/ - * - * @param mixed[]|Expr $expression - */ - public function anyElementTrue($expression): self; - - /** - * Returns the element at the specified array index. - * - * The expression can be any valid expression as long as it resolves - * to an array. - * The expression can be any valid expression as long as it resolves - * to an integer. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayElemAt/ - * - * @param mixed|Expr $array - * @param mixed|Expr $index - */ - public function arrayElemAt($array, $index): self; - - /** - * Converts an array into a single document. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/ - * - * @param mixed|Expr $array - */ - public function arrayToObject($array): self; - - /** - * Returns the inverse sin (arc sine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/asin/ - * - * @param mixed|Expr $expression - */ - public function asin($expression): self; - - /** - * Returns the inverse hyperbolic sine (hyperbolic arc sine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/asinh/ - * - * @param mixed|Expr $expression - */ - public function asinh($expression): self; - - /** - * Returns the inverse tangent (arc tangent) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atan/ - * - * @param mixed|Expr $expression - */ - public function atan($expression): self; - - /** - * Returns the inverse tangent (arc tangent) of y / x in radians, where y and x are the first and second values passed to the expression respectively. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atan2/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function atan2($expression1, $expression2): self; - - /** - * Returns the inverse hyperbolic tangent (hyperbolic arc tangent) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atanh/ - * - * @param mixed|Expr $expression - */ - public function atanh($expression): self; - - /** - * Adds a case statement for a branch of the $switch operator. - * - * Requires {@link switch()} to be called first. The argument can be any - * valid expression that resolves to a boolean. If the result is not a - * boolean, it is coerced to a boolean value. - * - * @param mixed|Expr $expression - */ - public function case($expression): self; - - /** - * Returns the smallest integer greater than or equal to the specified number. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ceil/ - * - * @param mixed|Expr $number - */ - public function ceil($number): self; - - /** - * Compares two values and returns: - * -1 if the first value is less than the second. - * 1 if the first value is greater than the second. - * 0 if the two values are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cmp/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function cmp($expression1, $expression2): self; - - /** - * Concatenates strings and returns the concatenated string. - * - * The arguments can be any valid expression as long as they resolve to - * strings. If the argument resolves to a value of null or refers to a field - * that is missing, $concat returns null. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/concat/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function concat($expression1, $expression2, ...$expressions): self; - - /** - * Concatenates arrays to return the concatenated array. - * - * The expressions can be any valid expression as long as they - * resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/concatArrays/ - * - * @param mixed|Expr $array1 - * @param mixed|Expr $array2 - * @param mixed|Expr ...$arrays Additional expressions - */ - public function concatArrays($array1, $array2, ...$arrays): self; - - /** - * Evaluates a boolean expression to return one of the two specified return - * expressions. - * - * The arguments can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cond/ - * - * @param mixed|Expr $if - * @param mixed|Expr $then - * @param mixed|Expr $else - */ - public function cond($if, $then, $else): self; - - /** - * Converts a value to a specified type. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/convert/ - * - * @param mixed|Expr $input - * @param mixed|Expr $to - * @param mixed|Expr|null $onError - * @param mixed|Expr|null $onNull - */ - public function convert($input, $to, $onError = null, $onNull = null): self; - - /** - * Returns the cosine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cos/ - * - * @param mixed|Expr $expression - */ - public function cos($expression): self; - - /** - * Returns the hyperbolic cosine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cosh/ - * - * @param mixed|Expr $expression - */ - public function cosh($expression): self; - - /** - * Converts a date object to a string according to a user-specified format. - * - * The format string can be any string literal, containing 0 or more format - * specifiers. - * The date argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dateToString/ - * - * @param mixed|Expr $expression - */ - public function dateToString(string $format, $expression): self; - - /** - * Returns the day of the month for a date as a number between 1 and 31. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfMonth/ - * - * @param mixed|Expr $expression - */ - public function dayOfMonth($expression): self; - - /** - * Returns the day of the week for a date as a number between 1 (Sunday) and - * 7 (Saturday). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfWeek/ - * - * @param mixed|Expr $expression - */ - public function dayOfWeek($expression): self; - - /** - * Returns the day of the year for a date as a number between 1 and 366. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfYear/ - * - * @param mixed|Expr $expression - */ - public function dayOfYear($expression): self; - - /** - * Adds a default statement for the current $switch operator. - * - * Requires {@link switch()} to be called first. The argument can be any - * valid expression. - * - * Note: if no default is specified and no branch evaluates to true, the - * $switch operator throws an error. - * - * @param mixed|Expr $expression - */ - public function default($expression): self; - - /** - * Converts a value from degrees to radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/degreesToRadians/ - * - * @param mixed|Expr $expression - */ - public function degreesToRadians($expression): self; - - /** - * Divides one number by another and returns the result. The first argument - * is divided by the second argument. - * - * The arguments can be any valid expression as long as the resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/divide/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function divide($expression1, $expression2): self; - - /** - * Compares two values and returns whether the are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/eq/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function eq($expression1, $expression2): self; - - /** - * Raises Euler’s number to the specified exponent and returns the result. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/exp/ - * - * @param mixed|Expr $exponent - */ - public function exp($exponent): self; - - /** - * Allows any expression to be used as a field value. - * - * @see https://docs.mongodb.com/manual/meta/aggregation-quick-reference/#aggregation-expressions - * - * @param mixed|Expr $value - * - * @return self - */ - public function expression($value); - - /** - * Set the current field for building the expression. - * - * @return self - */ - public function field(string $fieldName); - - /** - * Selects a subset of the array to return based on the specified condition. - * - * Returns an array with only those elements that match the condition. The - * returned elements are in the original order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/filter/ - * - * @param mixed|Expr $input - * @param mixed|Expr $as - * @param mixed|Expr $cond - */ - public function filter($input, $as, $cond): self; - - /** - * Returns the value that results from applying an expression to the first - * document in a group of documents that share the same group by key. Only - * meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/first/ - * - * @param mixed|Expr $expression - */ - public function first($expression): self; - - /** - * Returns the largest integer less than or equal to the specified number. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/floor/ - * - * @param mixed|Expr $number - */ - public function floor($number): self; - - /** - * Compares two values and returns: - * true when the first value is greater than the second value. - * false when the first value is less than or equivalent to the second - * value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/gt/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function gt($expression1, $expression2): self; - - /** - * Compares two values and returns: - * true when the first value is greater than or equivalent to the second - * value. - * false when the first value is less than the second value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/gte/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function gte($expression1, $expression2): self; - - /** - * Returns the hour portion of a date as a number between 0 and 23. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/hour/ - * - * @param mixed|Expr $expression - */ - public function hour($expression): self; - - /** - * Evaluates an expression and returns the value of the expression if the - * expression evaluates to a non-null value. If the expression evaluates to - * a null value, including instances of undefined values or missing fields, - * returns the value of the replacement expression. - * - * The arguments can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/ - * - * @param mixed|Expr $expression - * @param mixed|Expr $replacementExpression - */ - public function ifNull($expression, $replacementExpression): self; - - /** - * Returns a boolean indicating whether a specified value is in an array. - * - * Unlike the $in query operator, the aggregation $in operator does not - * support matching by regular expressions. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/in/ - * - * @param mixed|Expr $expression - * @param mixed|Expr $arrayExpression - */ - public function in($expression, $arrayExpression): self; - - /** - * Searches an array for an occurrence of a specified value and returns the - * array index (zero-based) of the first occurrence. If the value is not - * found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfArray/ - * - * @param mixed|Expr $arrayExpression can be any valid expression as long as it resolves to an array - * @param mixed|Expr $searchExpression can be any valid expression - * @param mixed|Expr $start Optional. An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param mixed|Expr $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - */ - public function indexOfArray($arrayExpression, $searchExpression, $start = null, $end = null): self; - - /** - * Searches a string for an occurrence of a substring and returns the UTF-8 - * byte index (zero-based) of the first occurrence. If the substring is not - * found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfBytes/ - * - * @param mixed|Expr $stringExpression can be any valid expression as long as it resolves to a string - * @param mixed|Expr $substringExpression can be any valid expression as long as it resolves to a string - * @param string|int|null $start An integral number that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param string|int|null $end An integral number that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - */ - public function indexOfBytes($stringExpression, $substringExpression, $start = null, $end = null): self; - - /** - * Searches a string for an occurrence of a substring and returns the UTF-8 - * code point index (zero-based) of the first occurrence. If the substring is - * not found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfCP/ - * - * @param mixed|Expr $stringExpression can be any valid expression as long as it resolves to a string - * @param mixed|Expr $substringExpression can be any valid expression as long as it resolves to a string - * @param string|int|null $start An integral number that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param string|int|null $end An integral number that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - */ - public function indexOfCP($stringExpression, $substringExpression, $start = null, $end = null): self; - - /** - * Determines if the operand is an array. Returns a boolean. - * - * The can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isArray/ - * - * @param mixed|Expr $expression - */ - public function isArray($expression): self; - - /** - * Returns boolean true if the specified expression resolves to an integer, decimal, double, or long. - * Returns boolean false if the expression resolves to any other BSON type, null, or a missing field. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isNumber/ - * - * @param mixed|Expr $expression - */ - public function isNumber($expression): self; - - /** - * Returns the weekday number in ISO 8601 format, ranging from 1 (for Monday) - * to 7 (for Sunday). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoDayOfWeek/ - * - * @param mixed|Expr $expression - */ - public function isoDayOfWeek($expression): self; - - /** - * Returns the week number in ISO 8601 format, ranging from 1 to 53. - * - * Week numbers start at 1 with the week (Monday through Sunday) that - * contains the year’s first Thursday. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoWeek/ - * - * @param mixed|Expr $expression - */ - public function isoWeek($expression): self; - - /** - * Returns the year number in ISO 8601 format. - * - * The year starts with the Monday of week 1 (ISO 8601) and ends with the - * Sunday of the last week (ISO 8601). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoWeek/ - * - * @param mixed|Expr $expression - */ - public function isoWeekYear($expression): self; - - /** - * Returns the value that results from applying an expression to the last - * document in a group of documents that share the same group by a field. - * Only meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/last/ - * - * @param mixed|Expr $expression - */ - public function last($expression): self; - - /** - * Binds variables for use in the specified expression, and returns the - * result of the expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/let/ - * - * @param mixed|Expr $vars Assignment block for the variables accessible in the in expression. To assign a variable, specify a string for the variable name and assign a valid expression for the value. - * @param mixed|Expr $in the expression to evaluate - */ - public function let($vars, $in): self; - - /** - * Returns a value without parsing. Use for values that the aggregation - * pipeline may interpret as an expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/literal/ - * - * @param mixed|Expr $value - */ - public function literal($value): self; - - /** - * Calculates the natural logarithm ln (i.e loge) of a number and returns - * the result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ - * - * @param mixed|Expr $number - */ - public function ln($number): self; - - /** - * Calculates the log of a number in the specified base and returns the - * result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * The expression can be any valid expression as long as it resolves - * to a positive number greater than 1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ - * - * @param mixed|Expr $number - * @param mixed|Expr $base - */ - public function log($number, $base): self; - - /** - * Calculates the log base 10 of a number and returns the result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log10/ - * - * @param mixed|Expr $number - */ - public function log10($number): self; - - /** - * Compares two values and returns: - * true when the first value is less than the second value. - * false when the first value is greater than or equivalent to the second - * value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/lt/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function lt($expression1, $expression2): self; - - /** - * Compares two values and returns: - * true when the first value is less than or equivalent to the second value. - * false when the first value is greater than the second value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/lte/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function lte($expression1, $expression2): self; - - /** - * Removes whitespace characters, including null, or the specified characters from - * the beginning and end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ltrim/ - * - * @param mixed|Expr $input - * @param mixed|Expr $chars - */ - public function ltrim($input, $chars = null): self; - - /** - * Applies an expression to each item in an array and returns an array with - * the applied results. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/map/ - * - * @param mixed|Expr $input an expression that resolves to an array - * @param string $as The variable name for the items in the input array. The in expression accesses each item in the input array by this variable. - * @param mixed|Expr $in The expression to apply to each item in the input array. The expression accesses the item by its variable name. - */ - public function map($input, $as, $in): self; - - /** - * Returns the metadata associated with a document in a pipeline operations. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/meta/ - * - * @param mixed|Expr $metaDataKeyword - */ - public function meta($metaDataKeyword): self; - - /** - * Returns the millisecond portion of a date as an integer between 0 and 999. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/millisecond/ - * - * @param mixed|Expr $expression - */ - public function millisecond($expression): self; - - /** - * Returns the minute portion of a date as a number between 0 and 59. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/minute/ - * - * @param mixed|Expr $expression - */ - public function minute($expression): self; - - /** - * Divides one number by another and returns the remainder. The first - * argument is divided by the second argument. - * - * The arguments can be any valid expression as long as they resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/mod/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function mod($expression1, $expression2): self; - - /** - * Returns the month of a date as a number between 1 and 12. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/month/ - * - * @param mixed|Expr $expression - */ - public function month($expression): self; - - /** - * Multiplies numbers together and returns the result. - * - * The arguments can be any valid expression as long as they resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/multiply/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function multiply($expression1, $expression2, ...$expressions): self; - - /** - * Compares two values and returns: - * true when the values are not equivalent. - * false when the values are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ne/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function ne($expression1, $expression2): self; - - /** - * Evaluates a boolean and returns the opposite boolean value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/not/ - * - * @param mixed|Expr $expression - */ - public function not($expression): self; - - /** - * Converts a document to an array. The return array contains an element for each field/value pair - * in the original document. Each element in the return array is a document that contains - * two fields k and v:. - * The k field contains the field name in the original document. - * The v field contains the value of the field in the original document. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/objectToArray/ - * - * @param mixed|Expr $object - */ - public function objectToArray($object): self; - - /** - * Raises a number to the specified exponent and returns the result. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/pow/ - * - * @param mixed|Expr $number - * @param mixed|Expr $exponent - */ - public function pow($number, $exponent): self; - - /** - * Converts a value from radians to degrees. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/radiansToDegrees/ - * - * @param mixed|Expr $expression - */ - public function radiansToDegrees($expression): self; - - /** - * Returns an array whose elements are a generated sequence of numbers. - * - * $range generates the sequence from the specified starting number by successively incrementing the starting number by the specified step value up to but not including the end point. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/range/ - * - * @param mixed|Expr $start An integer that specifies the start of the sequence. Can be any valid expression that resolves to an integer. - * @param mixed|Expr $end An integer that specifies the exclusive upper limit of the sequence. Can be any valid expression that resolves to an integer. - * @param mixed|Expr $step Optional. An integer that specifies the increment value. Can be any valid expression that resolves to a non-zero integer. Defaults to 1. - */ - public function range($start, $end, $step = 1): self; - - /** - * Applies an expression to each element in an array and combines them into - * a single value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/ - * - * @param mixed|Expr $input can be any valid expression that resolves to an array - * @param mixed|Expr $initialValue the initial cumulative value set before in is applied to the first element of the input array - * @param mixed|Expr $in A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. - */ - public function reduce($input, $initialValue, $in): self; - - /** - * Accepts an array expression as an argument and returns an array with the - * elements in reverse order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/reverseArray/ - * - * @param mixed|Expr $expression - */ - public function reverseArray($expression): self; - - /** - * Rounds a number to a whole integer or to a specified decimal place. - * - * The argument can be any valid expression as long as it resolves - * to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/round/ - * - * @param mixed|Expr $number - * @param mixed|Expr|null $place - */ - public function round($number, $place = null): self; - - /** - * Removes whitespace characters, including null, or the specified characters from the end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/rtrim/ - * - * @param mixed|Expr $input - * @param mixed|Expr $chars - */ - public function rtrim($input, $chars = null): self; - - /** - * Returns the second portion of a date as a number between 0 and 59, but - * can be 60 to account for leap seconds. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/second/ - * - * @param mixed|Expr $expression - */ - public function second($expression): self; - - /** - * Takes two sets and returns an array containing the elements that only - * exist in the first set. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setDifference/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function setDifference($expression1, $expression2): self; - - /** - * Compares two or more arrays and returns true if they have the same - * distinct elements and false otherwise. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setEquals/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional sets - */ - public function setEquals($expression1, $expression2, ...$expressions): self; - - /** - * Takes two or more arrays and returns an array that contains the elements - * that appear in every input array. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setIntersection/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional sets - */ - public function setIntersection($expression1, $expression2, ...$expressions): self; - - /** - * Takes two arrays and returns true when the first array is a subset of the - * second, including when the first array equals the second array, and false otherwise. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setIsSubset/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function setIsSubset($expression1, $expression2): self; - - /** - * Takes two or more arrays and returns an array containing the elements - * that appear in any input array. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setUnion/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional sets - */ - public function setUnion($expression1, $expression2, ...$expressions): self; - - /** - * Returns the sine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sin/ - * - * @param mixed|Expr $expression - */ - public function sin($expression): self; - - /** - * Returns the hyperbolic sine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sinh/ - * - * @param mixed|Expr $expression - */ - public function sinh($expression): self; - - /** - * Counts and returns the total the number of items in an array. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/size/ - * - * @param mixed|Expr $expression - */ - public function size($expression): self; - - /** - * Returns a subset of an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/slice/ - * - * @param mixed|Expr $array - * @param mixed|Expr $n - * @param mixed|Expr|null $position - */ - public function slice($array, $n, $position = null): self; - - /** - * Divides a string into an array of substrings based on a delimiter. - * - * $split removes the delimiter and returns the resulting substrings as - * elements of an array. If the delimiter is not found in the string, $split - * returns the original string as the only element of an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/split/ - * - * @param mixed|Expr $string The string to be split. Can be any valid expression as long as it resolves to a string. - * @param mixed|Expr $delimiter The delimiter to use when splitting the string expression. Can be any valid expression as long as it resolves to a string. - */ - public function split($string, $delimiter): self; - - /** - * Calculates the square root of a positive number and returns the result as - * a double. - * - * The argument can be any valid expression as long as it resolves to a - * non-negative number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sqrt/ - * - * @param mixed|Expr $expression - */ - public function sqrt($expression): self; - - /** - * Returns the number of UTF-8 encoded bytes in the specified string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strLenBytes/ - * - * @param mixed|Expr $string - */ - public function strLenBytes($string): self; - - /** - * Returns the number of UTF-8 code points in the specified string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strLenCP/ - * - * @param mixed|Expr $string - */ - public function strLenCP($string): self; - - /** - * Performs case-insensitive comparison of two strings. Returns - * 1 if first string is “greater than” the second string. - * 0 if the two strings are equal. - * -1 if the first string is “less than” the second string. - * - * The arguments can be any valid expression as long as they resolve to strings. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strcasecmp/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function strcasecmp($expression1, $expression2): self; - - /** - * Returns a substring of a string, starting at a specified index position - * and including the specified number of characters. The index is zero-based. - * - * The arguments can be any valid expression as long as long as the first argument resolves to a string, and the second and third arguments resolve to integers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substr/ - * - * @param mixed|Expr $string - * @param mixed|Expr $start - * @param mixed|Expr $length - */ - public function substr($string, $start, $length): self; - - /** - * Returns the substring of a string. - * - * The substring starts with the character at the specified UTF-8 byte index - * (zero-based) in the string and continues for the number of bytes - * specified. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substrBytes/ - * - * @param mixed|Expr $string The string from which the substring will be extracted. Can be any valid expression as long as it resolves to a string. - * @param mixed|Expr $start Indicates the starting point of the substring. Can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer. - * @param mixed|Expr $count can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer - */ - public function substrBytes($string, $start, $count): self; - - /** - * Returns the substring of a string. - * - * The substring starts with the character at the specified UTF-8 code point - * (CP) index (zero-based) in the string for the number of code points - * specified. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substrBytes/ - * - * @param mixed|Expr $string The string from which the substring will be extracted. Can be any valid expression as long as it resolves to a string. - * @param mixed|Expr $start Indicates the starting point of the substring. Can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer. - * @param mixed|Expr $count can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer - */ - public function substrCP($string, $start, $count): self; - - /** - * Subtracts two numbers to return the difference. The second argument is - * subtracted from the first argument. - * - * The arguments can be any valid expression as long as they resolve to numbers and/or dates. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/subtract/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function subtract($expression1, $expression2): self; - - /** - * Evaluates a series of case expressions. When it finds an expression which - * evaluates to true, $switch executes a specified expression and breaks out - * of the control flow. - * - * To add statements, use the {@link case()}, {@link then()} and - * {@link default()} methods. - */ - public function switch(): self; - - /** - * Returns the tangent of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/tan/ - * - * @param mixed|Expr $expression - */ - public function tan($expression): self; - - /** - * Returns the hyperbolic tangent of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/tanh/ - * - * @param mixed|Expr $expression - */ - public function tanh($expression): self; - - /** - * Adds a case statement for the current branch of the $switch operator. - * - * Requires {@link case()} to be called first. The argument can be any valid - * expression. - * - * @param mixed|Expr $expression - */ - public function then($expression): self; - - /** - * Converts value to a boolean. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toBool/ - * - * @param mixed|Expr $expression - */ - public function toBool($expression): self; - - /** - * Converts value to a Date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDate/ - * - * @param mixed|Expr $expression - */ - public function toDate($expression): self; - - /** - * Converts value to a Decimal128. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDecimal/ - * - * @param mixed|Expr $expression - */ - public function toDecimal($expression): self; - - /** - * Converts value to a double. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDouble/ - * - * @param mixed|Expr $expression - */ - public function toDouble($expression): self; - - /** - * Converts value to an integer. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toInt/ - * - * @param mixed|Expr $expression - */ - public function toInt($expression): self; - - /** - * Converts value to a long. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toLong/ - * - * @param mixed|Expr $expression - */ - public function toLong($expression): self; - - /** - * Converts a string to lowercase, returning the result. - * - * The argument can be any expression as long as it resolves to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toLower/ - * - * @param mixed|Expr $expression - */ - public function toLower($expression): self; - - /** - * Converts value to an ObjectId. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toObjectId/ - * - * @param mixed|Expr $expression - */ - public function toObjectId($expression): self; - - /** - * Converts value to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toString/ - * - * @param mixed|Expr $expression - */ - public function toString($expression): self; - - /** - * Converts a string to uppercase, returning the result. - * - * The argument can be any expression as long as it resolves to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toUpper/ - * - * @param mixed|Expr $expression - */ - public function toUpper($expression): self; - - /** - * Removes whitespace characters, including null, or the specified characters from - * the beginning and end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/trim/ - * - * @param mixed|Expr $input - * @param mixed|Expr|null $chars - */ - public function trim($input, $chars = null): self; - - /** - * Truncates a number to its integer. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/trunc/ - * - * @param mixed|Expr $number - */ - public function trunc($number): self; - - /** - * Returns a string that specifies the BSON type of the argument. - * - * The argument can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/type/ - * - * @param mixed|Expr $expression - */ - public function type($expression): self; - - /** - * Returns the week of the year for a date as a number between 0 and 53. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/week/ - * - * @param mixed|Expr $expression - */ - public function week($expression): self; - - /** - * Returns the year portion of a date. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/year/ - * - * @param mixed|Expr $expression - */ - public function year($expression): self; - - /** - * Transposes an array of input arrays so that the first element of the - * output array would be an array containing, the first element of the first - * input array, the first element of the second input array, etc. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/zip/ - * - * @param mixed|Expr $inputs An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. - * @param bool|null $useLongestLength a boolean which specifies whether the length of the longest array determines the number of arrays in the output array - * @param mixed|Expr|null $defaults An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. - */ - public function zip($inputs, ?bool $useLongestLength = null, $defaults = null): self; -} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/GroupAccumulatorOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/GroupAccumulatorOperators.php new file mode 100644 index 0000000000..7dbcaa0aa8 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/GroupAccumulatorOperators.php @@ -0,0 +1,220 @@ + $sortBy + */ + public function bottom($output, $sortBy): static; + + /** + * Returns the n bottom elements within a group according to the specified + * sort order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/bottomN/ + * + * @param mixed|Expr $output + * @param array $sortBy + * @param mixed|Expr $n + */ + public function bottomN($output, $sortBy, $n): static; + + /** + * Returns the number of documents in a group. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/count/ + */ + public function countDocuments(): static; + + /** + * Returns the value that results from applying an expression to the first + * document in a group of documents. Only meaningful when documents are in + * a defined order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/first/ + * + * @param mixed|Expr $expression + */ + public function first($expression): static; + + /** + * Returns the value that results from applying an expression to the first n + * documents in a group of documents. Only meaningful when documents are in + * a defined order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/firstN/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $n + */ + public function firstN($expression, $n): static; + + /** + * Returns the value that results from applying an expression to the last + * document in a group of documents. Only meaningful when documents are in + * a defined order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/last/ + * + * @param mixed|Expr $expression + */ + public function last($expression): static; + + /** + * Returns the value that results from applying an expression to the last n + * documents in a group of documents. Only meaningful when documents are in + * a defined order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/lastN/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $n + */ + public function lastN($expression, $n): static; + + /** + * Returns the highest expression value for each group. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/max/ + * + * @param mixed|Expr $expression + */ + public function max($expression): static; + + /** + * Returns the highest n expression values for each group. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/maxN/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $n + */ + public function maxN($expression, $n): static; + + /** + * Returns a document created by combining the input documents for each + * group. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/mergeObjects/ + * + * @param mixed|Expr $expression + */ + public function mergeObjects($expression): static; + + /** + * Returns the lowest expression value for each group. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/min/ + * + * @param mixed|Expr $expression + */ + public function min($expression): static; + + /** + * Returns the lowest n expression values for each group. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/minN/ + * + * @param mixed|Expr $expression + * @param mixed|Expr $n + */ + public function minN($expression, $n): static; + + /** + * Returns an array of expression values for documents in each group. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/push/ + * + * @param mixed|Expr $expression + */ + public function push($expression): static; + + /** + * Returns the population standard deviation of the input values. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevPop/ + * + * @param mixed|Expr $expression + */ + public function stdDevPop($expression): static; + + /** + * Returns the sample standard deviation of the input values. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevSamp/ + * + * @param mixed|Expr $expression + */ + public function stdDevSamp($expression): static; + + /** + * Calculates the collective sum of numeric values. Ignores non-numeric + * values. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sum/ + * + * @param mixed|Expr $expression + */ + public function sum($expression): static; + + /** + * Returns the top element within a group according to the specified sort + * order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/top/ + * + * @param mixed|Expr $output + * @param array $sortBy + */ + public function top($output, $sortBy): static; + + /** + * Returns the n top elements within a group according to the specified sort + * order. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/topN/ + * + * @param mixed|Expr $output + * @param array $sortBy + * @param mixed|Expr $n + */ + public function topN($output, $sortBy, $n): static; +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/MiscOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/MiscOperators.php new file mode 100644 index 0000000000..7a6f7429d5 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/MiscOperators.php @@ -0,0 +1,73 @@ +getExpr()->accumulator(...func_get_args()); + + return $this; + } + + public function addToSet($expression): static + { + $this->getExpr()->addToSet(...func_get_args()); + + return $this; + } + + public function avg($expression, ...$expressions): static + { + $this->getExpr()->avg(...func_get_args()); + + return $this; + } + + public function bottom($output, $sortBy): static + { + $this->getExpr()->bottom(...func_get_args()); + + return $this; + } + + public function bottomN($output, $sortBy, $n): static + { + $this->getExpr()->bottomN(...func_get_args()); + + return $this; + } + + public function countDocuments(): static + { + $this->getExpr()->countDocuments(); + + return $this; + } + + public function first($expression): static + { + $this->getExpr()->first(...func_get_args()); + + return $this; + } + + public function firstN($expression, $n): static + { + $this->getExpr()->firstN(...func_get_args()); + + return $this; + } + + public function function($body, $args, $lang = 'js'): static + { + $this->getExpr()->function(...func_get_args()); + + return $this; + } + + public function last($expression): static + { + $this->getExpr()->last(...func_get_args()); + + return $this; + } + + public function lastN($expression, $n): static + { + $this->getExpr()->lastN(...func_get_args()); + + return $this; + } + + public function max($expression, ...$expressions): static + { + $this->getExpr()->max(...func_get_args()); + + return $this; + } + + public function maxN($expression, $n): static + { + $this->getExpr()->maxN(...func_get_args()); + + return $this; + } + + public function mergeObjects($expression, ...$expressions): static + { + $this->getExpr()->mergeObjects(...func_get_args()); + + return $this; + } + + public function min($expression, ...$expressions): static + { + $this->getExpr()->min(...func_get_args()); + + return $this; + } + + public function minN($expression, $n): static + { + $this->getExpr()->minN(...func_get_args()); + + return $this; + } + + public function push($expression): static + { + $this->getExpr()->push(...func_get_args()); + + return $this; + } + + public function stdDevPop($expression, ...$expressions): static + { + $this->getExpr()->stdDevPop(...func_get_args()); + + return $this; + } + + public function stdDevSamp($expression, ...$expressions): static + { + $this->getExpr()->stdDevSamp(...func_get_args()); + + return $this; + } + + public function sum($expression, ...$expressions): static + { + $this->getExpr()->sum(...func_get_args()); + + return $this; + } + + public function top($output, $sortBy): static + { + $this->getExpr()->top(...func_get_args()); + + return $this; + } + + public function topN($output, $sortBy, $n): static + { + $this->getExpr()->topN(...func_get_args()); + + return $this; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/SetOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/SetOperators.php new file mode 100644 index 0000000000..160ccc2a36 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Operator/SetOperators.php @@ -0,0 +1,112 @@ + can be any valid expression. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isArray/ + * + * @param mixed|Expr $expression + */ + public function isArray($expression): static; + + /** + * Returns boolean true if the specified expression resolves to an integer, + * decimal, double, or long. Returns boolean false if the expression + * resolves to any other BSON type, null, or a missing field. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isNumber/ + * + * @param mixed|Expr $expression + */ + public function isNumber($expression): static; + + /** + * Converts value to a boolean. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toBool/ + * + * @param mixed|Expr $expression + */ + public function toBool($expression): static; + + /** + * Converts value to a Date. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDate/ + * + * @param mixed|Expr $expression + */ + public function toDate($expression): static; + + /** + * Converts value to a Decimal128. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDecimal/ + * + * @param mixed|Expr $expression + */ + public function toDecimal($expression): static; + + /** + * Converts value to a double. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDouble/ + * + * @param mixed|Expr $expression + */ + public function toDouble($expression): static; + + /** + * Converts value to an integer. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toInt/ + * + * @param mixed|Expr $expression + */ + public function toInt($expression): static; + + /** + * Converts value to a long. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toLong/ + * + * @param mixed|Expr $expression + */ + public function toLong($expression): static; + + /** + * Converts value to an ObjectId. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toObjectId/ + * + * @param mixed|Expr $expression + */ + public function toObjectId($expression): static; + + /** + * Converts value to a string. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toString/ + * + * @param mixed|Expr $expression + */ + public function toString($expression): static; + + /** + * Returns a string that specifies the BSON type of the argument. + * + * The argument can be any valid expression. + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/type/ + * + * @param mixed|Expr $expression + */ + public function type($expression): static; +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php index 28c3e777b2..6900ca1ff7 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage.php @@ -13,6 +13,9 @@ * Fluent interface for building aggregation pipelines. * * @internal + * + * @psalm-import-type PipelineExpression from Builder + * @psalm-type StageExpression = non-empty-array|object */ abstract class Stage { @@ -136,6 +139,16 @@ public function count(string $fieldName): Stage\Count return $this->builder->count($fieldName); } + /** + * Creates new documents in a sequence of documents where certain values in a field are missing. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/densify/ + */ + public function densify(string $fieldName): Stage\Densify + { + return $this->builder->densify($fieldName); + } + /** * Processes multiple aggregation pipelines within a single stage on the * same set of input documents. @@ -148,6 +161,16 @@ public function facet(): Stage\Facet return $this->builder->facet(); } + /** + * Populates null and missing field values within documents. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/fill/ + */ + public function fill(): Stage\Fill + { + return $this->builder->fill(); + } + /** * Outputs documents in order of nearest to farthest from a specified point. * You can only use this as the first stage of a pipeline. @@ -166,6 +189,7 @@ public function geoNear($x, $y = null): Stage\GeoNear * Returns the assembled aggregation pipeline * * @return array> + * @psalm-return PipelineExpression */ public function getPipeline(): array { @@ -242,6 +266,17 @@ public function match(): Stage\MatchStage return $this->builder->match(); } + /** + * Writes the results of the aggregation pipeline to a specified collection. + * The $merge operator must be the last stage in the pipeline. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/merge/ + */ + public function merge(): Stage\Merge + { + return $this->builder->merge(); + } + /** * Takes the documents returned by the aggregation pipeline and writes them * to a specified collection. This must be the last stage in the pipeline. @@ -292,10 +327,28 @@ public function replaceRoot($expression = null): Stage\ReplaceRoot return $this->builder->replaceRoot($expression); } + /** + * Replaces the input document with the specified document. The operation + * replaces all existing fields in the input document, including the _id + * field. With $replaceWith, you can promote an embedded document to the + * top-level. You can also specify a new document as the replacement. + * + * The $replaceWith stage is an alias for $replaceRoot. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/replaceWith/ + * + * @param string|mixed[]|Expr|null $expression Optional. A replacement expression that + * resolves to a document. + */ + public function replaceWith($expression = null): Stage\ReplaceWith + { + return $this->builder->replaceWith($expression); + } + /** * Controls if resulting iterator should be wrapped with CachingIterator. */ - public function rewindable(bool $rewindable = true): self + public function rewindable(bool $rewindable = true): static { $this->builder->rewindable($rewindable); @@ -312,6 +365,30 @@ public function sample(int $size): Stage\Sample return $this->builder->sample($size); } + /** + * The $search stage performs a full-text search on the specified field or + * fields which must be covered by an Atlas Search index. + * + * @see https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/#mongodb-pipeline-pipe.-search + */ + public function search(): Stage\Search + { + return $this->builder->search(); + } + + /** + * Adds new fields to documents. $set outputs documents that contain all + * existing fields from the input documents and newly added fields. + * + * The $set stage is an alias for $addFields. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/set/ + */ + public function set(): Stage\Set + { + return $this->builder->set(); + } + /** * Skips over the specified number of documents that pass into the stage and * passes the remaining documents to the next stage in the pipeline. @@ -350,6 +427,28 @@ public function sort($fieldName, $order = null): Stage\Sort return $this->builder->sort($fieldName, $order); } + /** + * Performs a union of two collections. $unionWith combines pipeline results + * from two collections into a single result set. The stage outputs the + * combined result set (including duplicates) to the next stage. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/unionWith/ + */ + public function unionWith(string $collection): Stage\UnionWith + { + return $this->builder->unionWith($collection); + } + + /** + * Removes/excludes fields from documents. + * + * @see https://www.mongodb.com/docs/rapid/reference/operator/aggregation/unset/ + */ + public function unset(string ...$fields): Stage\UnsetStage + { + return $this->builder->unset(...$fields); + } + /** * Deconstructs an array field from the input documents to output a document * for each element. Each output document is the input document with the diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AbstractBucket.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AbstractBucket.php index 40e4c2d441..bf3a74fd95 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AbstractBucket.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AbstractBucket.php @@ -24,21 +24,14 @@ */ abstract class AbstractBucket extends Stage { - private DocumentManager $dm; - - private ClassMetadata $class; - /** @var Bucket\AbstractOutput|null */ protected $output; /** @var Expr|array|string */ protected $groupBy; - public function __construct(Builder $builder, DocumentManager $documentManager, ClassMetadata $class) + public function __construct(Builder $builder, private DocumentManager $dm, private ClassMetadata $class) { - $this->dm = $documentManager; - $this->class = $class; - parent::__construct($builder); } @@ -48,7 +41,7 @@ public function __construct(Builder $builder, DocumentManager $documentManager, * * @param array|Expr|string $expression */ - public function groupBy($expression): self + public function groupBy($expression): static { $this->groupBy = $expression; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AbstractReplace.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AbstractReplace.php new file mode 100644 index 0000000000..1f39d4f95f --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AbstractReplace.php @@ -0,0 +1,55 @@ +dm->getUnitOfWork()->getDocumentPersister($this->class->name); + } + + /** @return array|string */ + protected function getReplaceExpression() + { + return $this->expression !== null ? $this->convertExpression($this->expression) : $this->expr->getExpression(); + } + + /** + * @param mixed[]|string|mixed $expression + * + * @return mixed[]|string|mixed + */ + private function convertExpression($expression) + { + if (is_array($expression)) { + return array_map([$this, 'convertExpression'], $expression); + } + + if (is_string($expression) && substr($expression, 0, 1) === '$') { + return '$' . $this->getDocumentPersister()->prepareFieldName(substr($expression, 1)); + } + + return Type::convertPHPToDatabaseValue(Expr::convertExpression($expression)); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AddFields.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AddFields.php index 7bf77b2b76..b658656bb9 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AddFields.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AddFields.php @@ -4,15 +4,19 @@ namespace Doctrine\ODM\MongoDB\Aggregation\Stage; +use Doctrine\ODM\MongoDB\Aggregation\Expr; + /** * Fluent interface for adding a $addFields stage to an aggregation pipeline. + * + * @psalm-import-type OperatorExpression from Expr + * @psalm-type AddFieldsStageExpression = array{'$addFields': array} */ final class AddFields extends Operator { + /** @return AddFieldsStageExpression */ public function getExpression(): array { - return [ - '$addFields' => $this->expr->getExpression(), - ]; + return ['$addFields' => $this->expr->getExpression()]; } } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket.php index 90d291945c..eac3323e8c 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket.php @@ -6,7 +6,6 @@ use function assert; -/** @method Bucket groupBy($expression) */ class Bucket extends AbstractBucket { /** @var mixed[] */ @@ -26,7 +25,7 @@ class Bucket extends AbstractBucket * * @param mixed $boundaries */ - public function boundaries(...$boundaries): self + public function boundaries(...$boundaries): static { $this->boundaries = $boundaries; @@ -40,7 +39,7 @@ public function boundaries(...$boundaries): self * * @param mixed $default */ - public function defaultBucket($default): self + public function defaultBucket($default): static { $this->default = $default; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php index 89cde5600b..c9942abc82 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php @@ -6,6 +6,8 @@ use Doctrine\ODM\MongoDB\Aggregation\Builder; use Doctrine\ODM\MongoDB\Aggregation\Expr; +use Doctrine\ODM\MongoDB\Aggregation\Operator\GroupAccumulatorOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ProvidesGroupAccumulatorOperators; use Doctrine\ODM\MongoDB\Aggregation\Stage; /** @@ -13,8 +15,10 @@ * * @internal */ -abstract class AbstractOutput extends Stage +abstract class AbstractOutput extends Stage implements GroupAccumulatorOperators { + use ProvidesGroupAccumulatorOperators; + /** @var Stage\AbstractBucket */ protected $bucket; @@ -33,46 +37,6 @@ public function getExpression(): array return $this->expr->getExpression(); } - /** - * Returns an array of all unique values that results from applying an - * expression to each document in a group of documents that share the same - * group by key. Order of the elements in the output array is unspecified. - * - * AddToSet is an accumulator operation only available in the group stage. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/addToSet/ - * @see Expr::addToSet - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function addToSet($expression) - { - $this->expr->addToSet($expression); - - return $this; - } - - /** - * Returns the average value of the numeric values that result from applying - * a specified expression to each document in a group of documents that - * share the same group by key. Ignores nun-numeric values. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/avg/ - * @see Expr::avg - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function avg($expression) - { - $this->expr->avg($expression); - - return $this; - } - /** * Used to use an expression as field value. Can be any expression. * @@ -83,7 +47,7 @@ public function avg($expression) * * @return $this */ - public function expression($value) + public function expression($value): static { $this->expr->expression($value); @@ -99,159 +63,15 @@ public function expression($value) * * @return $this */ - public function field($fieldName) + public function field($fieldName): static { $this->expr->field($fieldName); return $this; } - /** - * Returns the value that results from applying an expression to the first - * document in a group of documents that share the same group by key. Only - * meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/first/ - * @see Expr::first - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function first($expression) - { - $this->expr->first($expression); - - return $this; - } - - /** - * Returns the value that results from applying an expression to the last - * document in a group of documents that share the same group by a field. - * Only meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/last/ - * @see Expr::last - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function last($expression) - { - $this->expr->last($expression); - - return $this; - } - - /** - * Returns the highest value that results from applying an expression to - * each document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/max/ - * @see Expr::max - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function max($expression) + protected function getExpr(): Expr { - $this->expr->max($expression); - - return $this; - } - - /** - * Returns the lowest value that results from applying an expression to each - * document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/min/ - * @see Expr::min - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function min($expression) - { - $this->expr->min($expression); - - return $this; - } - - /** - * Returns an array of all values that result from applying an expression to - * each document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/push/ - * @see Expr::push - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function push($expression) - { - $this->expr->push($expression); - - return $this; - } - - /** - * Calculates the population standard deviation of the input values. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevPop/ - * @see Expr::stdDevPop - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function stdDevPop($expression) - { - $this->expr->stdDevPop($expression); - - return $this; - } - - /** - * Calculates the sample standard deviation of the input values. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevSamp/ - * @see Expr::stdDevSamp - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function stdDevSamp($expression) - { - $this->expr->stdDevSamp($expression); - - return $this; - } - - /** - * Calculates and returns the sum of all the numeric values that result from - * applying a specified expression to each document in a group of documents - * that share the same group by key. Ignores nun-numeric values. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sum/ - * @see Expr::sum - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function sum($expression) - { - $this->expr->sum($expression); - - return $this; + return $this->expr; } } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/BucketAuto.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/BucketAuto.php index 7611053c0e..87045c4f94 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/BucketAuto.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/BucketAuto.php @@ -6,7 +6,6 @@ use function assert; -/** @method BucketAuto groupBy($expression) */ class BucketAuto extends AbstractBucket { private ?int $buckets = null; @@ -17,7 +16,7 @@ class BucketAuto extends AbstractBucket * A positive 32-bit integer that specifies the number of buckets into which * input documents are grouped. */ - public function buckets(int $buckets): self + public function buckets(int $buckets): static { $this->buckets = $buckets; @@ -29,7 +28,7 @@ public function buckets(int $buckets): self * the calculated boundary edges end on preferred round numbers or their * powers of 10. */ - public function granularity(string $granularity): self + public function granularity(string $granularity): static { $this->granularity = $granularity; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/CollStats.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/CollStats.php index 9d787b6429..688e40c612 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/CollStats.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/CollStats.php @@ -9,6 +9,13 @@ /** * Fluent interface for adding a $collStats stage to an aggregation pipeline. + * + * @psalm-type CollStatsStageExpression = array{ + * '$collStats': array{ + * latencyStats?: array{histograms?: bool}, + * storageStats?: array{}, + * } + * } */ class CollStats extends Stage { @@ -28,7 +35,7 @@ public function __construct(Builder $builder) /** * Adds latency statistics to the return document. */ - public function showLatencyStats(bool $histograms = false): self + public function showLatencyStats(bool $histograms = false): static { $this->latencyStats = $histograms ? self::LATENCY_STATS_HISTOGRAMS : self::LATENCY_STATS_SIMPLE; @@ -38,13 +45,14 @@ public function showLatencyStats(bool $histograms = false): self /** * Adds storage statistics to the return document. */ - public function showStorageStats(): self + public function showStorageStats(): static { $this->storageStats = true; return $this; } + /** @psalm-return CollStatsStageExpression */ public function getExpression(): array { $collStats = []; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Count.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Count.php index 647833e373..7a9e55e3f5 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Count.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Count.php @@ -9,18 +9,17 @@ /** * Fluent interface for adding a $count stage to an aggregation pipeline. + * + * @psalm-type CountStageExpression = array{'$count': string} */ class Count extends Stage { - private string $fieldName; - - public function __construct(Builder $builder, string $fieldName) + public function __construct(Builder $builder, private string $fieldName) { parent::__construct($builder); - - $this->fieldName = $fieldName; } + /** @psalm-return CountStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php new file mode 100644 index 0000000000..0e054433d2 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php @@ -0,0 +1,88 @@ +, + * range: object{ + * bounds?: BoundsType, + * step?: int|float, + * unit?: UnitType + * } + * } + * } + */ +class Densify extends Stage +{ + private string $field; + + /** @var array */ + private array $partitionByFields = []; + + private object $range; + + public function __construct(Builder $builder, string $fieldName) + { + parent::__construct($builder); + + $this->field = $fieldName; + $this->range = (object) []; + } + + public function partitionByFields(string ...$fields): static + { + $this->partitionByFields = array_values($fields); + + return $this; + } + + /** + * @param array|string $bounds + * @param int|float $step + * @psalm-param BoundsType $bounds + * @psalm-param ''|UnitType $unit + */ + public function range($bounds, $step, string $unit = ''): static + { + $this->range = (object) [ + 'bounds' => $bounds, + 'step' => $step, + ]; + + if ($unit !== '') { + $this->range->unit = $unit; + } + + return $this; + } + + /** @psalm-return DensifyStageExpression */ + public function getExpression(): array + { + $params = (object) [ + 'field' => $this->field, + 'range' => $this->range, + ]; + + if ($this->partitionByFields) { + $params->partitionByFields = $this->partitionByFields; + } + + return ['$densify' => $params]; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Facet.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Facet.php index 4d2ef13cab..1096f0d040 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Facet.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Facet.php @@ -13,6 +13,9 @@ /** * Fluent interface for adding a $facet stage to an aggregation pipeline. + * + * @psalm-import-type PipelineExpression from Builder + * @psalm-type FacetStageExpression = array{'$facet': array} */ class Facet extends Stage { @@ -31,7 +34,7 @@ public function getExpression(): array /** * Set the current field for building the pipeline stage. */ - public function field(string $field): self + public function field(string $field): static { $this->field = $field; @@ -43,11 +46,11 @@ public function field(string $field): self * * @param Builder|Stage $builder */ - public function pipeline($builder): self + public function pipeline($builder): static { /** @psalm-suppress RedundantPropertyInitializationCheck because the property might not be set yet */ if (! isset($this->field)) { - throw new LogicException(__METHOD__ . ' requires you set a current field using field().'); + throw new LogicException(__METHOD__ . ' requires setting a current field using field().'); } if ($builder instanceof Stage) { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill.php new file mode 100644 index 0000000000..f136cbb3d2 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill.php @@ -0,0 +1,116 @@ + + * @psalm-type FillStageExpression = array{ + * '$fill': array{ + * partitionBy?: string|OperatorExpression, + * partitionByFields?: list, + * sortBy?: SortShape, + * output: array, + * } + * } + */ +class Fill extends Stage +{ + /** @var mixed|Expr|null */ + private $partitionBy = null; + + /** @var array */ + private array $partitionByFields = []; + + /** @var array */ + private array $sortBy = []; + + private Output $output; + + public function __construct(Builder $builder) + { + parent::__construct($builder); + + $this->output = new Output($this->builder, $this); + } + + /** @param mixed|Expr $expression */ + public function partitionBy($expression): static + { + $this->partitionBy = $expression; + + return $this; + } + + public function partitionByFields(string ...$fields): static + { + $this->partitionByFields = array_values($fields); + + return $this; + } + + /** + * @param array|string $fieldName Field name or array of field/order pairs + * @param int|string $order Field order (if one field is specified) + * @psalm-param SortShape|string $fieldName + * @psalm-param SortDirection|null $order + */ + public function sortBy($fieldName, $order = null): static + { + $fields = is_array($fieldName) ? $fieldName : [$fieldName => $order ?? 1]; + + foreach ($fields as $fieldName => $order) { + if (is_string($order)) { + $order = strtolower($order) === 'asc' ? 1 : -1; + } + + $this->sortBy[$fieldName] = $order; + } + + return $this; + } + + public function output(): Output + { + return $this->output; + } + + public function getExpression(): array + { + $params = (object) [ + 'output' => (object) $this->output->getExpression(), + ]; + + if ($this->partitionBy) { + $params->partitionBy = $this->partitionBy instanceof Expr + ? $this->partitionBy->getExpression() + : $this->partitionBy; + } + + if ($this->partitionByFields) { + $params->partitionByFields = $this->partitionByFields; + } + + if ($this->sortBy) { + $params->sortBy = (object) $this->sortBy; + } + + return ['$fill' => $params]; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill/Output.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill/Output.php new file mode 100644 index 0000000000..d975df84eb --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Fill/Output.php @@ -0,0 +1,107 @@ +> */ + private $output = []; + + public function __construct(Builder $builder, private Fill $fill) + { + parent::__construct($builder); + } + + /** @param mixed|Expr $expression */ + public function partitionBy($expression): Fill + { + return $this->fill->partitionBy($expression); + } + + public function partitionByFields(string ...$fields): Fill + { + return $this->fill->partitionByFields(...$fields); + } + + /** + * @param array|string $fieldName Field name or array of field/order pairs + * @param int|string $order Field order (if one field is specified) + * @psalm-param SortShape|string $fieldName + */ + public function sortBy($fieldName, $order = null): Fill + { + return $this->fill->sortBy(...func_get_args()); + } + + /** + * Set the current field for building the expression. + */ + public function field(string $fieldName): static + { + $this->currentField = $fieldName; + + return $this; + } + + public function linear(): static + { + $this->requiresCurrentField(__METHOD__); + $this->output[$this->currentField] = ['method' => 'linear']; + + return $this; + } + + public function locf(): static + { + $this->requiresCurrentField(__METHOD__); + $this->output[$this->currentField] = ['method' => 'locf']; + + return $this; + } + + /** @param mixed|Expr $expression */ + public function value($expression): static + { + $this->requiresCurrentField(__METHOD__); + $this->output[$this->currentField] = [ + 'value' => $expression instanceof Expr ? $expression->getExpression() : $expression, + ]; + + return $this; + } + + public function getExpression(): array + { + return $this->output; + } + + /** + * Ensure that a current field has been set. + * + * @throws LogicException if a current field has not been set. + */ + private function requiresCurrentField(string $method): void + { + if (! $this->currentField) { + throw new LogicException(sprintf('%s requires setting a current field using field().', $method)); + } + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GeoNear.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GeoNear.php index c2d4959afb..5de79b710b 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GeoNear.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GeoNear.php @@ -73,7 +73,7 @@ public function getExpression(): array /** * The output field that contains the calculated distance. To specify a field within an embedded document, use dot notation. */ - public function distanceField(string $distanceField): self + public function distanceField(string $distanceField): static { $this->distanceField = $distanceField; @@ -83,7 +83,7 @@ public function distanceField(string $distanceField): self /** * The factor to multiply all distances returned by the query. */ - public function distanceMultiplier(float $distanceMultiplier): self + public function distanceMultiplier(float $distanceMultiplier): static { $this->distanceMultiplier = $distanceMultiplier; @@ -93,7 +93,7 @@ public function distanceMultiplier(float $distanceMultiplier): self /** * This specifies the output field that identifies the location used to calculate the distance. */ - public function includeLocs(string $includeLocs): self + public function includeLocs(string $includeLocs): static { $this->includeLocs = $includeLocs; @@ -103,7 +103,7 @@ public function includeLocs(string $includeLocs): self /** * The maximum number of documents to return. */ - public function limit(int $limit): self + public function limit(int $limit): static { return $this->num($limit); } @@ -111,7 +111,7 @@ public function limit(int $limit): self /** * The maximum distance from the center point that the documents can be. */ - public function maxDistance(float $maxDistance): self + public function maxDistance(float $maxDistance): static { $this->maxDistance = $maxDistance; @@ -121,7 +121,7 @@ public function maxDistance(float $maxDistance): self /** * The minimum distance from the center point that the documents can be. */ - public function minDistance(float $minDistance): self + public function minDistance(float $minDistance): static { $this->minDistance = $minDistance; @@ -139,7 +139,7 @@ public function minDistance(float $minDistance): self * @param float|array|Point $x * @param float $y */ - public function near($x, $y = null): self + public function near($x, $y = null): static { if ($x instanceof Point) { $x = $x->jsonSerialize(); @@ -154,7 +154,7 @@ public function near($x, $y = null): self /** * The maximum number of documents to return. */ - public function num(int $num): self + public function num(int $num): static { $this->num = $num; @@ -164,7 +164,7 @@ public function num(int $num): self /** * Required if using a 2dsphere index. Determines how MongoDB calculates the distance. */ - public function spherical(bool $spherical = true): self + public function spherical(bool $spherical = true): static { $this->spherical = $spherical; @@ -174,7 +174,7 @@ public function spherical(bool $spherical = true): self /** * If this value is true, the query returns a matching document once, even if more than one of the document’s location fields match the query. */ - public function uniqueDocs(bool $uniqueDocs = true): self + public function uniqueDocs(bool $uniqueDocs = true): static { $this->uniqueDocs = $uniqueDocs; diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php index 77908f3045..4e75de85a1 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php @@ -40,22 +40,16 @@ class GraphLookup extends Stage private Stage\GraphLookup\MatchStage $restrictSearchWithMatch; - private DocumentManager $dm; - - private ClassMetadata $class; - private ?ClassMetadata $targetClass = null; /** * @param string $from Target collection for the $graphLookup operation to * search, recursively matching the connectFromField to the connectToField. */ - public function __construct(Builder $builder, string $from, DocumentManager $documentManager, ClassMetadata $class) + public function __construct(Builder $builder, string $from, private DocumentManager $dm, private ClassMetadata $class) { parent::__construct($builder); - $this->dm = $documentManager; - $this->class = $class; $this->restrictSearchWithMatch = new GraphLookup\MatchStage($this->builder, $this); $this->from($from); } @@ -66,7 +60,7 @@ public function __construct(Builder $builder, string $from, DocumentManager $doc * Contains the documents traversed in the $graphLookup stage to reach the * document. */ - public function alias(string $alias): self + public function alias(string $alias): static { $this->as = $alias; @@ -80,7 +74,7 @@ public function alias(string $alias): self * Optionally, connectFromField may be an array of field names, each of * which is individually followed through the traversal process. */ - public function connectFromField(string $connectFromField): self + public function connectFromField(string $connectFromField): static { // No targetClass mapping - simply use field name as is if (! $this->targetClass) { @@ -111,7 +105,7 @@ public function connectFromField(string $connectFromField): self * Field name in other documents against which to match the value of the * field specified by the connectFromField parameter. */ - public function connectToField(string $connectToField): self + public function connectToField(string $connectToField): static { $this->connectToField = $this->convertTargetFieldName($connectToField); @@ -125,7 +119,7 @@ public function connectToField(string $connectToField): self * represented as a NumberLong. Recursion depth value starts at zero, so the * first lookup corresponds to zero depth. */ - public function depthField(string $depthField): self + public function depthField(string $depthField): static { $this->depthField = $depthField; @@ -138,7 +132,7 @@ public function depthField(string $depthField): self * * @psalm-param class-string|string $from */ - public function from(string $from): self + public function from(string $from): static { // $from can either be // a) a field name indicating a reference to a different document. Currently, only REFERENCE_STORE_AS_ID is supported @@ -152,7 +146,7 @@ public function from(string $from): self // Check if mapped class with given name exists try { $this->targetClass = $this->dm->getClassMetadata($from); - } catch (BaseMappingException $e) { + } catch (BaseMappingException) { $this->from = $from; return $this; @@ -192,7 +186,7 @@ public function getExpression(): array /** * Non-negative integral number specifying the maximum recursion depth. */ - public function maxDepth(int $maxDepth): self + public function maxDepth(int $maxDepth): static { $this->maxDepth = $maxDepth; @@ -216,7 +210,7 @@ public function restrictSearchWithMatch(): GraphLookup\MatchStage * * @param string|mixed[]|Expr $expression */ - public function startWith($expression): self + public function startWith($expression): static { $this->startWith = $expression; @@ -224,7 +218,7 @@ public function startWith($expression): self } /** @throws MappingException */ - private function fromReference(string $fieldName): self + private function fromReference(string $fieldName): static { if (! $this->class->hasReference($fieldName)) { MappingException::referenceMappingNotFound($this->class->name, $fieldName); diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup/MatchStage.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup/MatchStage.php index dbd6194061..85d726d833 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup/MatchStage.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup/MatchStage.php @@ -11,13 +11,9 @@ class MatchStage extends BaseMatchStage { - private GraphLookup $graphLookup; - - public function __construct(Builder $builder, GraphLookup $graphLookup) + public function __construct(Builder $builder, private GraphLookup $graphLookup) { parent::__construct($builder); - - $this->graphLookup = $graphLookup; } public function getExpression(): array diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Group.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Group.php index 88bb883dbe..f57ef9a32b 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Group.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Group.php @@ -6,12 +6,18 @@ use Doctrine\ODM\MongoDB\Aggregation\Builder; use Doctrine\ODM\MongoDB\Aggregation\Expr; +use Doctrine\ODM\MongoDB\Aggregation\Operator\GroupAccumulatorOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ProvidesGroupAccumulatorOperators; /** * Fluent interface for adding a $group stage to an aggregation pipeline. + * + * @psalm-type GroupStageExpression = array{'$group': array} */ -class Group extends Operator +class Group extends Operator implements GroupAccumulatorOperators { + use ProvidesGroupAccumulatorOperators; + /** @var Expr */ protected $expr; @@ -22,6 +28,7 @@ public function __construct(Builder $builder) $this->expr = $builder->expr(); } + /** @psalm-return GroupStageExpression */ public function getExpression(): array { return [ @@ -29,174 +36,8 @@ public function getExpression(): array ]; } - /** - * Returns an array of all unique values that results from applying an - * expression to each document in a group of documents that share the same - * group by key. Order of the elements in the output array is unspecified. - * - * AddToSet is an accumulator operation only available in the group stage. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/addToSet/ - * @see Expr::addToSet - * - * @param mixed|Expr $expression - * - * @return $this - */ - public function addToSet($expression): self - { - $this->expr->addToSet($expression); - - return $this; - } - - /** - * Returns the average value of the numeric values that result from applying - * a specified expression to each document in a group of documents that - * share the same group by key. Ignores nun-numeric values. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/avg/ - * @see Expr::avg - * - * @param mixed|Expr $expression - */ - public function avg($expression): self - { - $this->expr->avg($expression); - - return $this; - } - - /** - * Returns the value that results from applying an expression to the first - * document in a group of documents that share the same group by key. Only - * meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/first/ - * @see Expr::first - * - * @param mixed|Expr $expression - */ - public function first($expression): self - { - $this->expr->first($expression); - - return $this; - } - - /** - * Returns the value that results from applying an expression to the last - * document in a group of documents that share the same group by a field. - * Only meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/last/ - * @see Expr::last - * - * @param mixed|Expr $expression - */ - public function last($expression): self - { - $this->expr->last($expression); - - return $this; - } - - /** - * Returns the highest value that results from applying an expression to - * each document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/max/ - * @see Expr::max - * - * @param mixed|Expr $expression - */ - public function max($expression): self - { - $this->expr->max($expression); - - return $this; - } - - /** - * Returns the lowest value that results from applying an expression to each - * document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/min/ - * @see Expr::min - * - * @param mixed|Expr $expression - */ - public function min($expression): self - { - $this->expr->min($expression); - - return $this; - } - - /** - * Returns an array of all values that result from applying an expression to - * each document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/push/ - * @see Expr::push - * - * @param mixed|Expr $expression - */ - public function push($expression): self - { - $this->expr->push($expression); - - return $this; - } - - /** - * Calculates the population standard deviation of the input values. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevPop/ - * @see Expr::stdDevPop - * - * @param mixed|Expr $expression - */ - public function stdDevPop($expression): self + protected function getExpr(): Expr { - $this->expr->stdDevPop($expression); - - return $this; - } - - /** - * Calculates the sample standard deviation of the input values. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevSamp/ - * @see Expr::stdDevSamp - * - * @param mixed|Expr $expression - */ - public function stdDevSamp($expression): self - { - $this->expr->stdDevSamp($expression); - - return $this; - } - - /** - * Calculates and returns the sum of all the numeric values that result from - * applying a specified expression to each document in a group of documents - * that share the same group by key. Ignores nun-numeric values. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sum/ - * @see Expr::sum - * - * @param mixed|Expr $expression - */ - public function sum($expression): self - { - $this->expr->sum($expression); - - return $this; + return $this->expr; } } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/IndexStats.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/IndexStats.php index 2235f3af38..f59e7732fb 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/IndexStats.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/IndexStats.php @@ -9,9 +9,12 @@ /** * Fluent interface for adding a $indexStats stage to an aggregation pipeline. + * + * @psalm-type IndexStatsStageExpression = array{'$indexStats': object} */ class IndexStats extends Stage { + /** @psalm-return IndexStatsStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Limit.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Limit.php index ec2aa15596..7081749470 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Limit.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Limit.php @@ -9,18 +9,17 @@ /** * Fluent interface for adding a $limit stage to an aggregation pipeline. + * + * @psalm-type LimitStageExpression = array{'$limit': int} */ class Limit extends Stage { - private int $limit; - - public function __construct(Builder $builder, int $limit) + public function __construct(Builder $builder, private int $limit) { parent::__construct($builder); - - $this->limit = $limit; } + /** @psalm-return LimitStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php index a241a216f4..c2d4455bdf 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php @@ -15,13 +15,22 @@ /** * Fluent interface for building aggregation pipelines. + * + * @psalm-import-type PipelineExpression from Builder + * @psalm-type PipelineParamType = Builder|Stage|PipelineExpression + * @psalm-type LookupStageExpression = array{ + * $lookup: array{ + * from: string, + * 'as'?: string, + * localField?: string, + * foreignField?: string, + * pipeline?: PipelineExpression, + * let?: array, + * } + * } */ class Lookup extends Stage { - private DocumentManager $dm; - - private ClassMetadata $class; - private ?ClassMetadata $targetClass = null; private string $from; @@ -32,21 +41,21 @@ class Lookup extends Stage private ?string $as = null; - /** @var array|null */ + /** @var array|null */ private ?array $let = null; - /** @var Builder|array>|null */ + /** + * @var Builder|array>|null + * @psalm-var Builder|PipelineExpression|null + */ private $pipeline = null; private bool $excludeLocalAndForeignField = false; - public function __construct(Builder $builder, string $from, DocumentManager $documentManager, ClassMetadata $class) + public function __construct(Builder $builder, string $from, private DocumentManager $dm, private ClassMetadata $class) { parent::__construct($builder); - $this->dm = $documentManager; - $this->class = $class; - $this->from($from); } @@ -57,7 +66,7 @@ public function __construct(Builder $builder, string $from, DocumentManager $doc * collection. If the specified name already exists in the input document, * the existing field is overwritten. */ - public function alias(string $alias): self + public function alias(string $alias): static { $this->as = $alias; @@ -67,7 +76,7 @@ public function alias(string $alias): self /** * Specifies the collection or field name in the same database to perform the join with. */ - public function from(string $from): self + public function from(string $from): static { // $from can either be // a) a field name indicating a reference to a different document. Currently, only REFERENCE_STORE_AS_ID is supported @@ -81,7 +90,7 @@ public function from(string $from): self // Check if mapped class with given name exists try { $this->targetClass = $this->dm->getClassMetadata($from); - } catch (BaseMappingException $e) { + } catch (BaseMappingException) { $this->from = $from; return $this; @@ -92,33 +101,40 @@ public function from(string $from): self return $this; } + /** @psalm-return LookupStageExpression */ public function getExpression(): array { - $expression = [ - '$lookup' => [ - 'from' => $this->from, - 'as' => $this->as, - ], + $lookup = [ + 'from' => $this->from, ]; + if ($this->as !== null) { + $lookup['as'] = $this->as; + } + if (! $this->excludeLocalAndForeignField) { - $expression['$lookup']['localField'] = $this->localField; - $expression['$lookup']['foreignField'] = $this->foreignField; + if ($this->localField !== null) { + $lookup['localField'] = $this->localField; + } + + if ($this->foreignField !== null) { + $lookup['foreignField'] = $this->foreignField; + } } if (! empty($this->let)) { - $expression['$lookup']['let'] = $this->let; + $lookup['let'] = $this->let; } if ($this->pipeline !== null) { if ($this->pipeline instanceof Builder) { - $expression['$lookup']['pipeline'] = $this->pipeline->getPipeline(false); + $lookup['pipeline'] = $this->pipeline->getPipeline(false); } else { - $expression['$lookup']['pipeline'] = $this->pipeline; + $lookup['pipeline'] = $this->pipeline; } } - return $expression; + return ['$lookup' => $lookup]; } /** @@ -129,7 +145,7 @@ public function getExpression(): array * contain the localField, the $lookup treats the field as having a value of * null for matching purposes. */ - public function localField(string $localField): self + public function localField(string $localField): static { $this->localField = $this->prepareFieldName($localField, $this->class); @@ -144,7 +160,7 @@ public function localField(string $localField): self * contain the foreignField, the $lookup treats the value as null for * matching purposes. */ - public function foreignField(string $foreignField): self + public function foreignField(string $foreignField): static { $this->foreignField = $this->prepareFieldName($foreignField, $this->targetClass); @@ -157,9 +173,9 @@ public function foreignField(string $foreignField): self * Use the variable expressions to access the fields from * the joined collection's documents that are input to the pipeline. * - * @param array $let + * @param array $let */ - public function let(array $let): self + public function let(array $let): static { $this->let = $let; @@ -177,8 +193,9 @@ public function let(array $let): self * and then reference the variables in the pipeline stages. * * @param Builder|Stage|array> $pipeline + * @psalm-param PipelineParamType $pipeline */ - public function pipeline($pipeline): self + public function pipeline($pipeline): static { if ($pipeline instanceof Stage) { $this->pipeline = $pipeline->builder; @@ -196,7 +213,7 @@ public function pipeline($pipeline): self /** * Excludes localField and foreignField from an expression. */ - public function excludeLocalAndForeignField(): self + public function excludeLocalAndForeignField(): static { $this->excludeLocalAndForeignField = true; @@ -213,7 +230,7 @@ protected function prepareFieldName(string $fieldName, ?ClassMetadata $class = n } /** @throws MappingException */ - private function fromReference(string $fieldName): self + private function fromReference(string $fieldName): static { if (! $this->class->hasReference($fieldName)) { MappingException::referenceMappingNotFound($this->class->name, $fieldName); diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/MatchStage.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/MatchStage.php index 953c8451f0..2e8d7bef0f 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/MatchStage.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/MatchStage.php @@ -43,10 +43,8 @@ public function __clone() * * @param mixed[]|Expr $expression * @param mixed[]|Expr ...$expressions - * - * @return static */ - public function addAnd($expression, ...$expressions): self + public function addAnd($expression, ...$expressions): static { $this->query->addAnd(...func_get_args()); @@ -64,10 +62,8 @@ public function addAnd($expression, ...$expressions): self * * @param mixed[]|Expr $expression * @param mixed[]|Expr ...$expressions - * - * @return static */ - public function addNor($expression, ...$expressions): self + public function addNor($expression, ...$expressions): static { $this->query->addNor(...func_get_args()); @@ -85,10 +81,8 @@ public function addNor($expression, ...$expressions): self * * @param mixed[]|Expr $expression * @param mixed[]|Expr ...$expressions - * - * @return static */ - public function addOr($expression, ...$expressions): self + public function addOr($expression, ...$expressions): static { $this->query->addOr(...func_get_args()); @@ -102,10 +96,8 @@ public function addOr($expression, ...$expressions): self * @see https://docs.mongodb.com/manual/reference/operator/all/ * * @param mixed[] $values - * - * @return static */ - public function all(array $values): self + public function all(array $values): static { $this->query->all($values); @@ -122,10 +114,8 @@ public function all(array $values): self * @see https://docs.mongodb.com/manual/reference/operator/elemMatch/ * * @param mixed[]|Expr $expression - * - * @return static */ - public function elemMatch($expression): self + public function elemMatch($expression): static { $this->query->elemMatch($expression); @@ -138,10 +128,8 @@ public function elemMatch($expression): self * @see Expr::equals() * * @param mixed $value - * - * @return static */ - public function equals($value): self + public function equals($value): static { $this->query->equals($value); @@ -153,10 +141,8 @@ public function equals($value): self * * @see Expr::exists() * @see https://docs.mongodb.com/manual/reference/operator/exists/ - * - * @return static */ - public function exists(bool $bool): self + public function exists(bool $bool): static { $this->query->exists($bool); @@ -176,10 +162,8 @@ public function expr(): Expr * Set the current field for building the expression. * * @see Expr::field() - * - * @return static */ - public function field(string $field): self + public function field(string $field): static { $this->query->field($field); @@ -196,10 +180,8 @@ public function field(string $field): self * @see https://docs.mongodb.com/manual/reference/operator/geoIntersects/ * * @param array|Geometry $geometry - * - * @return static */ - public function geoIntersects($geometry): self + public function geoIntersects($geometry): static { $this->query->geoIntersects($geometry); @@ -214,10 +196,8 @@ public function geoIntersects($geometry): self * * @see Expr::geoWithin() * @see https://docs.mongodb.com/manual/reference/operator/geoWithin/ - * - * @return static */ - public function geoWithin(Geometry $geometry): self + public function geoWithin(Geometry $geometry): static { $this->query->geoWithin($geometry); @@ -235,10 +215,8 @@ public function geoWithin(Geometry $geometry): self * * @see Expr::geoWithinBox() * @see https://docs.mongodb.com/manual/reference/operator/box/ - * - * @return static */ - public function geoWithinBox(float $x1, float $y1, float $x2, float $y2): self + public function geoWithinBox(float $x1, float $y1, float $x2, float $y2): static { $this->query->geoWithinBox($x1, $y1, $x2, $y2); @@ -253,10 +231,8 @@ public function geoWithinBox(float $x1, float $y1, float $x2, float $y2): self * * @see Expr::geoWithinCenter() * @see https://docs.mongodb.com/manual/reference/operator/center/ - * - * @return static */ - public function geoWithinCenter(float $x, float $y, float $radius): self + public function geoWithinCenter(float $x, float $y, float $radius): static { $this->query->geoWithinCenter($x, $y, $radius); @@ -270,10 +246,8 @@ public function geoWithinCenter(float $x, float $y, float $radius): self * * @see Expr::geoWithinCenterSphere() * @see https://docs.mongodb.com/manual/reference/operator/centerSphere/ - * - * @return static */ - public function geoWithinCenterSphere(float $x, float $y, float $radius): self + public function geoWithinCenterSphere(float $x, float $y, float $radius): static { $this->query->geoWithinCenterSphere($x, $y, $radius); @@ -298,10 +272,8 @@ public function geoWithinCenterSphere(float $x, float $y, float $radius): self * @param array{int|float, int|float} $point2 Second point of the polygon * @param array{int|float, int|float} $point3 Third point of the polygon * @param array{int|float, int|float} ...$points Additional points of the polygon - * - * @return static */ - public function geoWithinPolygon($point1, $point2, $point3, ...$points): self + public function geoWithinPolygon($point1, $point2, $point3, ...$points): static { $this->query->geoWithinPolygon(...func_get_args()); @@ -322,10 +294,8 @@ public function getExpression(): array * @see https://docs.mongodb.com/manual/reference/operator/gt/ * * @param mixed $value - * - * @return static */ - public function gt($value): self + public function gt($value): static { $this->query->gt($value); @@ -339,10 +309,8 @@ public function gt($value): self * @see https://docs.mongodb.com/manual/reference/operator/gte/ * * @param mixed $value - * - * @return static */ - public function gte($value): self + public function gte($value): static { $this->query->gte($value); @@ -356,18 +324,15 @@ public function gte($value): self * @see https://docs.mongodb.com/manual/reference/operator/in/ * * @param mixed[] $values - * - * @return static */ - public function in(array $values): self + public function in(array $values): static { $this->query->in($values); return $this; } - /** @return static */ - public function includesReferenceTo(object $document): self + public function includesReferenceTo(object $document): static { $this->query->includesReferenceTo($document); @@ -381,10 +346,8 @@ public function includesReferenceTo(object $document): self * * @see Expr::language() * @see https://docs.mongodb.com/manual/reference/operator/text/ - * - * @return static */ - public function language(string $language): self + public function language(string $language): static { $this->query->language($language); @@ -398,10 +361,8 @@ public function language(string $language): self * @see https://docs.mongodb.com/manual/reference/operator/lte/ * * @param mixed $value - * - * @return static */ - public function lt($value): self + public function lt($value): static { $this->query->lt($value); @@ -415,10 +376,8 @@ public function lt($value): self * @see https://docs.mongodb.com/manual/reference/operator/lte/ * * @param mixed $value - * - * @return static */ - public function lte($value): self + public function lte($value): static { $this->query->lte($value); @@ -433,10 +392,8 @@ public function lte($value): self * * @param float|int $divisor * @param float|int $remainder - * - * @return static */ - public function mod($divisor, $remainder = 0): self + public function mod($divisor, $remainder = 0): static { $this->query->mod($divisor, $remainder); @@ -453,10 +410,8 @@ public function mod($divisor, $remainder = 0): self * @see https://docs.mongodb.com/manual/reference/operator/not/ * * @param mixed[]|Expr $expression - * - * @return static */ - public function not($expression): self + public function not($expression): static { $this->query->not($expression); @@ -470,10 +425,8 @@ public function not($expression): self * @see https://docs.mongodb.com/manual/reference/operator/ne/ * * @param mixed $value - * - * @return static */ - public function notEqual($value): self + public function notEqual($value): static { $this->query->notEqual($value); @@ -487,10 +440,8 @@ public function notEqual($value): self * @see https://docs.mongodb.com/manual/reference/operator/nin/ * * @param mixed[] $values - * - * @return static */ - public function notIn(array $values): self + public function notIn(array $values): static { $this->query->notIn($values); @@ -507,18 +458,15 @@ public function notIn(array $values): self * * @param mixed $start * @param mixed $end - * - * @return static */ - public function range($start, $end): self + public function range($start, $end): static { $this->query->range($start, $end); return $this; } - /** @return static */ - public function references(object $document): self + public function references(object $document): static { $this->query->references($document); @@ -530,10 +478,8 @@ public function references(object $document): self * * @see Expr::size() * @see https://docs.mongodb.com/manual/reference/operator/size/ - * - * @return static */ - public function size(int $size): self + public function size(int $size): static { $this->query->size($size); @@ -549,10 +495,8 @@ public function size(int $size): self * * @see Expr::text() * @see https://docs.mongodb.com/master/reference/operator/query/text/ - * - * @return static */ - public function text(string $search): self + public function text(string $search): static { $this->query->text($search); @@ -566,10 +510,8 @@ public function text(string $search): self * @see https://docs.mongodb.com/manual/reference/operator/type/ * * @param int|string $type - * - * @return static */ - public function type($type): self + public function type($type): static { $this->query->type($type); diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php new file mode 100644 index 0000000000..0db46342c7 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php @@ -0,0 +1,158 @@ +, + * let?: array, + * whenMatched?: WhenMatchedType, + * whenNotMatched?: WhenNotMatchedType, + * } + * } + */ +class Merge extends Stage +{ + /** + * @var string|array + * @psalm-var OutputCollection + */ + private $into; + + /** @var list */ + private array $on = []; + + /** @var array */ + private array $let = []; + + /** + * @var string|array|Builder|Stage + * @psalm-var WhenMatchedParamType + */ + private $whenMatched; + + private ?string $whenNotMatched = null; + + public function __construct(Builder $builder, private DocumentManager $dm) + { + parent::__construct($builder); + } + + /** @psalm-return MergeStageExpression */ + public function getExpression(): array + { + $params = (object) [ + 'into' => $this->into, + ]; + + if ($this->on) { + $params->on = count($this->on) === 1 ? $this->on[0] : $this->on; + } + + if ($this->let) { + $params->let = $this->let; + } + + if ($this->whenMatched) { + $params->whenMatched = $this->whenMatched instanceof Builder + ? $this->whenMatched->getPipeline(false) + : $this->whenMatched; + } + + if ($this->whenNotMatched) { + $params->whenNotMatched = $this->whenNotMatched; + } + + return ['$merge' => $params]; + } + + /** + * @param string|array $collection + * @psalm-param OutputCollection $collection + */ + public function into($collection): static + { + if (is_array($collection)) { + $this->into = $collection; + + return $this; + } + + try { + $class = $this->dm->getClassMetadata($collection); + $this->into = $class->getCollection(); + } catch (MappingException) { + $this->into = $collection; + } + + return $this; + } + + /** + * Optional. Specifies variables to use in the pipeline stages. + * + * Use the variable expressions to access the fields from + * the joined collection's documents that are input to the pipeline. + * + * @param array $let + */ + public function let(array $let): static + { + $this->let = $let; + + return $this; + } + + public function on(string ...$fields): static + { + $this->on = array_values($fields); + + return $this; + } + + /** + * @param string|array|Builder|Stage $whenMatched + * @psalm-param WhenMatchedParamType $whenMatched + */ + public function whenMatched($whenMatched): static + { + if ($whenMatched instanceof Stage) { + $this->whenMatched = $whenMatched->builder; + } else { + $this->whenMatched = $whenMatched; + } + + if ($this->builder === $this->whenMatched) { + throw new InvalidArgumentException('Cannot use the same Builder instance for $merge whenMatched pipeline.'); + } + + return $this; + } + + public function whenNotMatched(string $whenNotMatched): static + { + $this->whenNotMatched = $whenNotMatched; + + return $this; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Operator.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Operator.php index cb91581164..452828b5c1 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Operator.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Operator.php @@ -6,15 +6,46 @@ use Doctrine\ODM\MongoDB\Aggregation\Builder; use Doctrine\ODM\MongoDB\Aggregation\Expr; -use Doctrine\ODM\MongoDB\Aggregation\Operator\GenericOperatorsInterface; +use Doctrine\ODM\MongoDB\Aggregation\Operator\AccumulatorOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ArithmeticOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ArrayOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\BooleanOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ComparisonOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ConditionalOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\DataSizeOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\DateOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\MiscOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\ObjectOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\SetOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\StringOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\TimestampOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\TrigonometryOperators; +use Doctrine\ODM\MongoDB\Aggregation\Operator\TypeOperators; use Doctrine\ODM\MongoDB\Aggregation\Stage; use function func_get_args; /** * Fluent interface for adding operators to aggregation stages. + * + * @internal */ -abstract class Operator extends Stage implements GenericOperatorsInterface +abstract class Operator extends Stage implements + AccumulatorOperators, + ArithmeticOperators, + ArrayOperators, + BooleanOperators, + ComparisonOperators, + ConditionalOperators, + DataSizeOperators, + DateOperators, + MiscOperators, + ObjectOperators, + SetOperators, + StringOperators, + TimestampOperators, + TrigonometryOperators, + TypeOperators { /** @var Expr */ protected $expr; @@ -26,71 +57,28 @@ public function __construct(Builder $builder) $this->expr = $builder->expr(); } - /** - * Returns the absolute value of a number. - * - * The argument can be any valid expression as long as it resolves - * to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/abs/ - * @see Expr::abs - * - * @param mixed|Expr $number - * - * @return static - */ - public function abs($number): self + public function abs($number): static { - $this->expr->abs($number); + $this->expr->abs(...func_get_args()); return $this; } - /** - * Returns the inverse cosine (arc cosine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/acos/ - * - * @param mixed|Expr $expression - */ - public function acos($expression): self + public function acos($expression): static { - $this->expr->acos($expression); + $this->expr->acos(...func_get_args()); return $this; } - /** - * Returns the inverse hyperbolic cosine (hyperbolic arc cosine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/acosh/ - * - * @param mixed|Expr $expression - */ - public function acosh($expression): self + public function acosh($expression): static { - $this->expr->acosh($expression); + $this->expr->acosh(...func_get_args()); return $this; } - /** - * Adds numbers together or adds numbers and a date. If one of the arguments - * is a date, $add treats the other arguments as milliseconds to add to the - * date. - * - * The arguments can be any valid expression as long as they resolve to either all numbers or to numbers and a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/add/ - * @see Expr::add - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional expressions - * - * @return static - */ - public function add($expression1, $expression2, ...$expressions): self + public function add($expression1, $expression2, ...$expressions): static { $this->expr->add(...func_get_args()); @@ -105,10 +93,8 @@ public function add($expression1, $expression2, ...$expressions): self * * @param mixed[]|Expr $expression * @param mixed[]|Expr ...$expressions - * - * @return static */ - public function addAnd($expression, ...$expressions): self + public function addAnd($expression, ...$expressions): static { $this->expr->addAnd(...func_get_args()); @@ -123,2047 +109,1023 @@ public function addAnd($expression, ...$expressions): self * * @param mixed[]|Expr $expression * @param mixed[]|Expr ...$expressions - * - * @return static */ - public function addOr($expression, ...$expressions): self + public function addOr($expression, ...$expressions): static { $this->expr->addOr(...func_get_args()); return $this; } - /** - * Evaluates an array as a set and returns true if no element in the array - * is false. Otherwise, returns false. An empty array returns true. - * - * The expression must resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/allElementsTrue/ - * @see Expr::allElementsTrue - * - * @param mixed|Expr $expression - * - * @return static - */ - public function allElementsTrue($expression): self + public function allElementsTrue($expression): static { - $this->expr->allElementsTrue($expression); + $this->expr->allElementsTrue(...func_get_args()); return $this; } - /** - * Evaluates an array as a set and returns true if any of the elements are - * true and false otherwise. An empty array returns false. - * - * The expression must resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/anyElementTrue/ - * @see Expr::anyElementTrue - * - * @param mixed[]|Expr $expression - * - * @return static - */ - public function anyElementTrue($expression): self + public function and($expression, ...$expressions): static { - $this->expr->anyElementTrue($expression); + $this->expr->and(...func_get_args()); return $this; } - /** - * Returns the element at the specified array index. - * - * The expression can be any valid expression as long as it resolves - * to an array. - * The expression can be any valid expression as long as it resolves - * to an integer. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayElemAt/ - * @see Expr::arrayElemAt - * - * @param mixed|Expr $array - * @param mixed|Expr $index - * - * @return static - */ - public function arrayElemAt($array, $index): self + public function anyElementTrue($expression): static { - $this->expr->arrayElemAt($array, $index); + $this->expr->anyElementTrue(...func_get_args()); return $this; } - /** - * Converts an array into a single document. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/arrayToObject/ - * - * @param mixed|Expr $array - */ - public function arrayToObject($array): self + public function arrayElemAt($array, $index): static { - $this->expr->arrayToObject($array); + $this->expr->arrayElemAt(...func_get_args()); return $this; } - /** - * Returns the inverse tangent (arc tangent) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atan/ - * - * @param mixed|Expr $expression - */ - public function atan($expression): self + public function arrayToObject($array): static { - $this->expr->atan($expression); + $this->expr->arrayToObject(...func_get_args()); return $this; } - /** - * Returns the inverse sin (arc sine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/asin/ - * - * @param mixed|Expr $expression - */ - public function asin($expression): self + public function atan($expression): static { - $this->expr->asin($expression); + $this->expr->atan(...func_get_args()); return $this; } - /** - * Returns the inverse hyperbolic sine (hyperbolic arc sine) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/asinh/ - * - * @param mixed|Expr $expression - */ - public function asinh($expression): self + public function asin($expression): static { - $this->expr->asinh($expression); + $this->expr->asin(...func_get_args()); return $this; } - /** - * Returns the inverse tangent (arc tangent) of y / x in radians, where y and x are the first and second values passed to the expression respectively. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atan2/ - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - */ - public function atan2($expression1, $expression2): self + public function asinh($expression): static { - $this->expr->atan2($expression1, $expression2); + $this->expr->asinh(...func_get_args()); return $this; } - /** - * Returns the inverse hyperbolic tangent (hyperbolic arc tangent) of a value in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/atanh/ - * - * @param mixed|Expr $expression - */ - public function atanh($expression): self + public function atan2($expression1, $expression2): static { - $this->expr->atanh($expression); + $this->expr->atan2(...func_get_args()); return $this; } - /** - * Adds a case statement for a branch of the $switch operator. - * - * Requires {@link switch()} to be called first. The argument can be any - * valid expression that resolves to a boolean. If the result is not a - * boolean, it is coerced to a boolean value. - * - * @param mixed|Expr $expression - */ - public function case($expression): self + public function atanh($expression): static { - $this->expr->case($expression); + $this->expr->atanh(...func_get_args()); return $this; } - /** - * Returns the smallest integer greater than or equal to the specified number. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ceil/ - * @see Expr::ceil - * - * @param mixed|Expr $number - * - * @return static - */ - public function ceil($number): self + public function avg($expression, ...$expressions): static { - $this->expr->ceil($number); + $this->expr->avg(...func_get_args()); return $this; } - /** - * Compares two values and returns: - * -1 if the first value is less than the second. - * 1 if the first value is greater than the second. - * 0 if the two values are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cmp/ - * @see Expr::cmp - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function cmp($expression1, $expression2): self + public function binarySize($expression): static { - $this->expr->cmp($expression1, $expression2); + $this->expr->binarySize(...func_get_args()); return $this; } - /** - * Concatenates strings and returns the concatenated string. - * - * The arguments can be any valid expression as long as they resolve to - * strings. If the argument resolves to a value of null or refers to a field - * that is missing, $concat returns null. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/concat/ - * @see Expr::concat - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional expressions - * - * @return static - */ - public function concat($expression1, $expression2, ...$expressions): self + public function bsonSize($expression): static { - $this->expr->concat(...func_get_args()); + $this->expr->bsonSize(...func_get_args()); return $this; } - /** - * Concatenates arrays to return the concatenated array. - * - * The expressions can be any valid expression as long as they - * resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/concatArrays/ - * @see Expr::concatArrays - * - * @param mixed|Expr $array1 - * @param mixed|Expr $array2 - * @param mixed|Expr ...$arrays Additional expressions - * - * @return static - */ - public function concatArrays($array1, $array2, ...$arrays): self + public function case($expression): static { - $this->expr->concatArrays(...func_get_args()); + $this->expr->case(...func_get_args()); return $this; } - /** - * Evaluates a boolean expression to return one of the two specified return - * expressions. - * - * The arguments can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cond/ - * @see Expr::cond - * - * @param mixed|Expr $if - * @param mixed|Expr $then - * @param mixed|Expr $else - * - * @return static - */ - public function cond($if, $then, $else): self + public function ceil($number): static { - $this->expr->cond($if, $then, $else); + $this->expr->ceil(...func_get_args()); return $this; } - /** - * Converts a value to a specified type. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/convert/ - * - * @param mixed|Expr $input - * @param mixed|Expr $to - * @param mixed|Expr|null $onError - * @param mixed|Expr|null $onNull - */ - public function convert($input, $to, $onError = null, $onNull = null): self + public function cmp($expression1, $expression2): static { - $this->expr->convert($input, $to, $onError, $onNull); + $this->expr->cmp(...func_get_args()); return $this; } - /** - * Returns the cosine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cos/ - * - * @param mixed|Expr $expression - */ - public function cos($expression): self + public function concat($expression1, $expression2, ...$expressions): static { - $this->expr->cos($expression); + $this->expr->concat(...func_get_args()); return $this; } - /** - * Returns the hyperbolic cosine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/cosh/ - * - * @param mixed|Expr $expression - */ - public function cosh($expression): self + public function concatArrays($array1, $array2, ...$arrays): static { - $this->expr->cosh($expression); + $this->expr->concatArrays(...func_get_args()); return $this; } - /** - * Converts a date object to a string according to a user-specified format. - * - * The format string can be any string literal, containing 0 or more format - * specifiers. - * The date argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dateToString/ - * @see Expr::dateToString - * - * @param string $format - * @param mixed|Expr $expression - * - * @return static - */ - public function dateToString($format, $expression): self + public function cond($if, $then, $else): static { - $this->expr->dateToString($format, $expression); + $this->expr->cond(...func_get_args()); return $this; } - /** - * Returns the day of the month for a date as a number between 1 and 31. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfMonth/ - * @see Expr::dayOfMonth - * - * @param mixed|Expr $expression - * - * @return static - */ - public function dayOfMonth($expression): self + public function convert($input, $to, $onError = null, $onNull = null): static { - $this->expr->dayOfMonth($expression); + $this->expr->convert(...func_get_args()); return $this; } - /** - * Returns the day of the week for a date as a number between 1 (Sunday) and - * 7 (Saturday). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfWeek/ - * @see Expr::dayOfWeek - * - * @param mixed|Expr $expression - * - * @return static - */ - public function dayOfWeek($expression): self + public function cos($expression): static { - $this->expr->dayOfWeek($expression); + $this->expr->cos(...func_get_args()); return $this; } - /** - * Returns the day of the year for a date as a number between 1 and 366. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/dayOfYear/ - * @see Expr::dayOfYear - * - * @param mixed|Expr $expression - * - * @return static - */ - public function dayOfYear($expression): self + public function cosh($expression): static { - $this->expr->dayOfYear($expression); + $this->expr->cosh(...func_get_args()); return $this; } - /** - * Adds a default statement for the current $switch operator. - * - * Requires {@link switch()} to be called first. The argument can be any - * valid expression. - * - * Note: if no default is specified and no branch evaluates to true, the - * $switch operator throws an error. - * - * @param mixed|Expr $expression - */ - public function default($expression): self + public function dateAdd($startDate, $unit, $amount, $timezone = null): static { - $this->expr->default($expression); + $this->expr->dateAdd(...func_get_args()); return $this; } - /** - * Converts a value from degrees to radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/degreesToRadians/ - * - * @param mixed|Expr $expression - */ - public function degreesToRadians($expression): self + public function dateDiff($startDate, $endDate, $unit, $timezone = null, $startOfWeek = null): static { - $this->expr->degreesToRadians($expression); + $this->expr->dateDiff(...func_get_args()); return $this; } - /** - * Divides one number by another and returns the result. The first argument - * is divided by the second argument. - * - * The arguments can be any valid expression as long as the resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/divide/ - * @see Expr::divide - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function divide($expression1, $expression2): self + public function dateFromParts($year = null, $isoWeekYear = null, $month = null, $isoWeek = null, $day = null, $isoDayOfWeek = null, $hour = null, $minute = null, $second = null, $millisecond = null, $timezone = null): static { - $this->expr->divide($expression1, $expression2); + $this->expr->dateFromParts(...func_get_args()); return $this; } - /** - * Compares two values and returns whether they are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/eq/ - * @see Expr::eq - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function eq($expression1, $expression2): self + public function dateFromString($dateString, $format = null, $timezone = null, $onError = null, $onNull = null): static { - $this->expr->eq($expression1, $expression2); + $this->expr->dateFromString(...func_get_args()); return $this; } - /** - * Raises Euler’s number to the specified exponent and returns the result. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/exp/ - * @see Expr::exp - * - * @param mixed|Expr $exponent - * - * @return static - */ - public function exp($exponent): self + public function dateSubtract($startDate, $unit, $amount, $timezone = null): static { - $this->expr->exp($exponent); + $this->expr->dateSubtract(...func_get_args()); return $this; } - /** - * Used to use an expression as field value. Can be any expression - * - * @see https://docs.mongodb.com/manual/meta/aggregation-quick-reference/#aggregation-expressions - * @see Expr::expression - * - * @param mixed|Expr $value - * - * @return static - */ - public function expression($value) + public function dateToParts($date, $timezone = null, $iso8601 = null): static { - $this->expr->expression($value); + $this->expr->dateToParts(...func_get_args()); return $this; } - /** - * Set the current field for building the expression. - * - * @see Expr::field - * - * @return static - */ - public function field(string $fieldName) + public function dateToString(string $format, $expression, $timezone = null, $onNull = null): static { - $this->expr->field($fieldName); + $this->expr->dateToString(...func_get_args()); return $this; } - /** - * Selects a subset of the array to return based on the specified condition. - * - * Returns an array with only those elements that match the condition. The - * returned elements are in the original order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/filter/ - * @see Expr::filter - * - * @param mixed|Expr $input - * @param mixed|Expr $as - * @param mixed|Expr $cond - * - * @return static - */ - public function filter($input, $as, $cond): self + public function dateTrunc($date, $unit, $binSize = null, $timezone = null, $startOfWeek = null): static { - $this->expr->filter($input, $as, $cond); + $this->expr->dateTrunc(...func_get_args()); return $this; } - /** - * Returns the value that results from applying an expression to the first - * document in a group of documents that share the same group by key. Only - * meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/first/ - * - * @param mixed|Expr $expression - */ - public function first($expression): self + public function dayOfMonth($expression): static { - $this->expr->first($expression); + $this->expr->dayOfMonth(...func_get_args()); return $this; } - /** - * Returns the largest integer less than or equal to the specified number. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/floor/ - * @see Expr::floor - * - * @param mixed|Expr $number - * - * @return static - */ - public function floor($number): self + public function dayOfWeek($expression): static { - $this->expr->floor($number); + $this->expr->dayOfWeek(...func_get_args()); return $this; } - /** - * Compares two values and returns: - * true when the first value is greater than the second value. - * false when the first value is less than or equivalent to the second value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/gt/ - * @see Expr::gt - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function gt($expression1, $expression2): self + public function dayOfYear($expression): static { - $this->expr->gt($expression1, $expression2); + $this->expr->dayOfYear(...func_get_args()); return $this; } - /** - * Compares two values and returns: - * true when the first value is greater than or equivalent to the second value. - * false when the first value is less than the second value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/gte/ - * @see Expr::gte - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function gte($expression1, $expression2): self + public function default($expression): static { - $this->expr->gte($expression1, $expression2); + $this->expr->default(...func_get_args()); return $this; } - /** - * Returns the hour portion of a date as a number between 0 and 23. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/hour/ - * @see Expr::hour - * - * @param mixed|Expr $expression - * - * @return static - */ - public function hour($expression): self + public function degreesToRadians($expression): static { - $this->expr->hour($expression); + $this->expr->degreesToRadians(...func_get_args()); return $this; } - /** - * Returns a boolean indicating whether a specified value is in an array. - * - * Unlike the $in query operator, the aggregation $in operator does not - * support matching by regular expressions. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/in/ - * @see Expr::in - * - * @param mixed|Expr $expression - * @param mixed|Expr $arrayExpression - * - * @return static - */ - public function in($expression, $arrayExpression): self + public function divide($expression1, $expression2): static { - $this->expr->in($expression, $arrayExpression); + $this->expr->divide(...func_get_args()); return $this; } - /** - * Searches an array for an occurrence of a specified value and returns the - * array index (zero-based) of the first occurrence. If the value is not - * found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfArray/ - * @see Expr::indexOfArray - * - * @param mixed|Expr $arrayExpression Can be any valid expression as long as it resolves to an array. - * @param mixed|Expr $searchExpression Can be any valid expression. - * @param mixed|Expr $start Optional. An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param mixed|Expr $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * - * @return static - */ - public function indexOfArray($arrayExpression, $searchExpression, $start = null, $end = null): self + public function eq($expression1, $expression2): static { - $this->expr->indexOfArray($arrayExpression, $searchExpression, $start, $end); + $this->expr->eq(...func_get_args()); return $this; } - /** - * Searches a string for an occurrence of a substring and returns the UTF-8 - * byte index (zero-based) of the first occurrence. If the substring is not - * found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfBytes/ - * - * @param mixed|Expr $stringExpression Can be any valid expression as long as it resolves to a string. - * @param mixed|Expr $substringExpression Can be any valid expression as long as it resolves to a string. - * @param string|int|null $start An integral number that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param string|int|null $end An integral number that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * - * @return static - */ - public function indexOfBytes($stringExpression, $substringExpression, $start = null, $end = null): self + public function exp($exponent): static { - $this->expr->indexOfBytes($stringExpression, $substringExpression, $start, $end); + $this->expr->exp(...func_get_args()); return $this; } - /** - * Searches a string for an occurrence of a substring and returns the UTF-8 - * code point index (zero-based) of the first occurrence. If the substring is - * not found, returns -1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/indexOfCP/ - * - * @param mixed|Expr $stringExpression Can be any valid expression as long as it resolves to a string. - * @param mixed|Expr $substringExpression Can be any valid expression as long as it resolves to a string. - * @param string|int|null $start An integral number that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * @param string|int|null $end An integral number that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. - * - * @return static - */ - public function indexOfCP($stringExpression, $substringExpression, $start = null, $end = null): self + public function expression($value): static { - $this->expr->indexOfCP($stringExpression, $substringExpression, $start, $end); + $this->expr->expression(...func_get_args()); return $this; } /** - * Evaluates an expression and returns the value of the expression if the - * expression evaluates to a non-null value. If the expression evaluates to - * a null value, including instances of undefined values or missing fields, - * returns the value of the replacement expression. - * - * The arguments can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/ - * @see Expr::ifNull - * - * @param mixed|Expr $expression - * @param mixed|Expr $replacementExpression + * Set the current field for building the expression. * - * @return static + * @see Expr::field */ - public function ifNull($expression, $replacementExpression): self + public function field(string $fieldName): static { - $this->expr->ifNull($expression, $replacementExpression); + $this->expr->field(...func_get_args()); return $this; } - /** - * Determines if the operand is an array. Returns a boolean. - * - * The can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isArray/ - * @see Expr::isArray - * - * @param mixed|Expr $expression - * - * @return static - */ - public function isArray($expression): self + public function filter($input, $as, $cond): static { - $this->expr->isArray($expression); + $this->expr->filter(...func_get_args()); return $this; } - /** - * Returns boolean true if the specified expression resolves to an integer, decimal, double, or long. - * Returns boolean false if the expression resolves to any other BSON type, null, or a missing field. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isNumber/ - * - * @param mixed|Expr $expression - */ - public function isNumber($expression): self + public function first($expression): static { - $this->expr->isNumber($expression); + $this->expr->first(...func_get_args()); return $this; } - /** - * Returns the weekday number in ISO 8601 format, ranging from 1 (for Monday) - * to 7 (for Sunday). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoDayOfWeek/ - * - * @param mixed|Expr $expression - * - * @return static - */ - public function isoDayOfWeek($expression): self + public function firstN($expression, $n): static { - $this->expr->isoDayOfWeek($expression); + $this->expr->firstN(...func_get_args()); return $this; } - /** - * Returns the week number in ISO 8601 format, ranging from 1 to 53. - * - * Week numbers start at 1 with the week (Monday through Sunday) that - * contains the year’s first Thursday. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoWeek/ - * - * @param mixed|Expr $expression - * - * @return static - */ - public function isoWeek($expression): self + public function floor($number): static { - $this->expr->isoWeek($expression); + $this->expr->floor(...func_get_args()); return $this; } - /** - * Returns the year number in ISO 8601 format. - * - * The year starts with the Monday of week 1 (ISO 8601) and ends with the - * Sunday of the last week (ISO 8601). - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/isoWeek/ - * - * @param mixed|Expr $expression - * - * @return static - */ - public function isoWeekYear($expression): self + public function getField($field, $input = null): static { - $this->expr->isoWeekYear($expression); + $this->expr->getField(...func_get_args()); return $this; } - /** - * Returns the value that results from applying an expression to the last - * document in a group of documents that share the same group by a field. - * Only meaningful when documents are in a defined order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/last/ - * - * @param mixed|Expr $expression - */ - public function last($expression): self + public function gt($expression1, $expression2): static { - $this->expr->last($expression); + $this->expr->gt(...func_get_args()); return $this; } - /** - * Binds variables for use in the specified expression, and returns the - * result of the expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/let/ - * @see Expr::let - * - * @param mixed|Expr $vars Assignment block for the variables accessible in the in expression. To assign a variable, specify a string for the variable name and assign a valid expression for the value. - * @param mixed|Expr $in The expression to evaluate. - * - * @return static - */ - public function let($vars, $in): self + public function gte($expression1, $expression2): static { - $this->expr->let($vars, $in); + $this->expr->gte(...func_get_args()); return $this; } - /** - * Returns a value without parsing. Use for values that the aggregation - * pipeline may interpret as an expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/literal/ - * @see Expr::literal - * - * @param mixed|Expr $value - * - * @return static - */ - public function literal($value): self + public function hour($expression): static { - $this->expr->literal($value); + $this->expr->hour(...func_get_args()); return $this; } - /** - * Calculates the natural logarithm ln (i.e loge) of a number and returns - * the result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ - * @see Expr::ln - * - * @param mixed|Expr $number - * - * @return static - */ - public function ln($number): self + public function in($expression, $arrayExpression): static { - $this->expr->ln($number); + $this->expr->in(...func_get_args()); return $this; } - /** - * Calculates the log of a number in the specified base and returns the - * result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * The expression can be any valid expression as long as it resolves - * to a positive number greater than 1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ - * @see Expr::log - * - * @param mixed|Expr $number - * @param mixed|Expr $base - * - * @return static - */ - public function log($number, $base): self + public function indexOfArray($arrayExpression, $searchExpression, $start = null, $end = null): static { - $this->expr->log($number, $base); + $this->expr->indexOfArray(...func_get_args()); return $this; } - /** - * Calculates the log base 10 of a number and returns the result as a double. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * The expression can be any valid expression as long as it resolves - * to a positive number greater than 1. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/log/ - * @see Expr::log10 - * - * @param mixed|Expr $number - * - * @return static - */ - public function log10($number): self + public function indexOfBytes($stringExpression, $substringExpression, $start = null, $end = null): static { - $this->expr->log10($number); + $this->expr->indexOfBytes(...func_get_args()); return $this; } - /** - * Compares two values and returns: - * true when the first value is less than the second value. - * false when the first value is greater than or equivalent to the second value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/lt/ - * @see Expr::lt - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function lt($expression1, $expression2): self + public function indexOfCP($stringExpression, $substringExpression, $start = null, $end = null): static { - $this->expr->lt($expression1, $expression2); + $this->expr->indexOfCP(...func_get_args()); return $this; } - /** - * Compares two values and returns: - * true when the first value is less than or equivalent to the second value. - * false when the first value is greater than the second value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/lte/ - * @see Expr::lte - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function lte($expression1, $expression2): self + public function ifNull($expression, $replacementExpression): static { - $this->expr->lte($expression1, $expression2); + $this->expr->ifNull(...func_get_args()); return $this; } - /** - * Removes whitespace characters, including null, or the specified characters from - * the beginning and end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ltrim/ - * - * @param mixed|Expr $input - * @param mixed|Expr $chars - */ - public function ltrim($input, $chars = null): self + public function isArray($expression): static { - $this->expr->ltrim($input, $chars); + $this->expr->isArray(...func_get_args()); return $this; } - /** - * Applies an expression to each item in an array and returns an array with - * the applied results. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/map/ - * @see Expr::map - * - * @param mixed|Expr $input An expression that resolves to an array. - * @param string $as The variable name for the items in the input array. The in expression accesses each item in the input array by this variable. - * @param mixed|Expr $in The expression to apply to each item in the input array. The expression accesses the item by its variable name. - * - * @return static - */ - public function map($input, $as, $in): self + public function isNumber($expression): static { - $this->expr->map($input, $as, $in); + $this->expr->isNumber(...func_get_args()); return $this; } - /** - * Returns the metadata associated with a document in a pipeline operations. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/meta/ - * @see Expr::meta - * - * @param mixed|Expr $metaDataKeyword - * - * @return static - */ - public function meta($metaDataKeyword): self + public function isoDayOfWeek($expression): static { - $this->expr->meta($metaDataKeyword); + $this->expr->isoDayOfWeek(...func_get_args()); return $this; } - /** - * Returns the millisecond portion of a date as an integer between 0 and 999. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/millisecond/ - * @see Expr::millisecond - * - * @param mixed|Expr $expression - * - * @return static - */ - public function millisecond($expression): self + public function isoWeek($expression): static { - $this->expr->millisecond($expression); + $this->expr->isoWeek(...func_get_args()); return $this; } - /** - * Returns the minute portion of a date as a number between 0 and 59. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/minute/ - * @see Expr::minute - * - * @param mixed|Expr $expression - * - * @return static - */ - public function minute($expression): self + public function isoWeekYear($expression): static { - $this->expr->minute($expression); + $this->expr->isoWeekYear(...func_get_args()); return $this; } - /** - * Divides one number by another and returns the remainder. The first - * argument is divided by the second argument. - * - * The arguments can be any valid expression as long as they resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/mod/ - * @see Expr::mod - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function mod($expression1, $expression2): self + public function last($expression): static { - $this->expr->mod($expression1, $expression2); + $this->expr->last(...func_get_args()); return $this; } - /** - * Returns the month of a date as a number between 1 and 12. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/month/ - * @see Expr::month - * - * @param mixed|Expr $expression - * - * @return static - */ - public function month($expression): self + public function lastN($expression, $n): static { - $this->expr->month($expression); + $this->expr->lastN(...func_get_args()); return $this; } - /** - * Multiplies numbers together and returns the result. - * - * The arguments can be any valid expression as long as they resolve to numbers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/multiply/ - * @see Expr::multiply - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional expressions - * - * @return static - */ - public function multiply($expression1, $expression2, ...$expressions): self + public function let($vars, $in): static + { + $this->expr->let(...func_get_args()); + + return $this; + } + + public function literal($value): static + { + $this->expr->literal(...func_get_args()); + + return $this; + } + + public function ln($number): static + { + $this->expr->ln(...func_get_args()); + + return $this; + } + + public function log($number, $base): static + { + $this->expr->log(...func_get_args()); + + return $this; + } + + public function log10($number): static + { + $this->expr->log10(...func_get_args()); + + return $this; + } + + public function lt($expression1, $expression2): static + { + $this->expr->lt(...func_get_args()); + + return $this; + } + + public function lte($expression1, $expression2): static + { + $this->expr->lte(...func_get_args()); + + return $this; + } + + public function ltrim($input, $chars = null): static + { + $this->expr->ltrim(...func_get_args()); + + return $this; + } + + public function map($input, $as, $in): static + { + $this->expr->map(...func_get_args()); + + return $this; + } + + public function max($expression, ...$expressions): static + { + $this->expr->max(...func_get_args()); + + return $this; + } + + public function maxN($expression, $n): static + { + $this->expr->maxN(...func_get_args()); + + return $this; + } + + public function mergeObjects($expression, ...$expressions): static + { + $this->expr->mergeObjects(...func_get_args()); + + return $this; + } + + public function meta($metaDataKeyword): static + { + $this->expr->meta(...func_get_args()); + + return $this; + } + + public function millisecond($expression): static + { + $this->expr->millisecond(...func_get_args()); + + return $this; + } + + public function min($expression, ...$expressions): static + { + $this->expr->min(...func_get_args()); + + return $this; + } + + public function minN($expression, $n): static + { + $this->expr->minN(...func_get_args()); + + return $this; + } + + public function minute($expression): static + { + $this->expr->minute(...func_get_args()); + + return $this; + } + + public function mod($expression1, $expression2): static + { + $this->expr->mod(...func_get_args()); + + return $this; + } + + public function month($expression): static + { + $this->expr->month(...func_get_args()); + + return $this; + } + + public function multiply($expression1, $expression2, ...$expressions): static { $this->expr->multiply(...func_get_args()); return $this; } - /** - * Compares two values and returns: - * true when the values are not equivalent. - * false when the values are equivalent. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/ne/ - * @see Expr::ne - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function ne($expression1, $expression2): self + public function ne($expression1, $expression2): static { - $this->expr->ne($expression1, $expression2); + $this->expr->ne(...func_get_args()); return $this; } - /** - * Evaluates a boolean and returns the opposite boolean value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/not/ - * @see Expr::not - * - * @param mixed|Expr $expression - * - * @return static - */ - public function not($expression): self + public function not($expression): static { - $this->expr->not($expression); + $this->expr->not(...func_get_args()); return $this; } - /** - * Converts a document to an array. The return array contains an element for each field/value pair - * in the original document. Each element in the return array is a document that contains - * two fields k and v:. - * The k field contains the field name in the original document. - * The v field contains the value of the field in the original document. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/objectToArray/ - * - * @param mixed|Expr $object - */ - public function objectToArray($object): self + public function objectToArray($object): static { - $this->expr->objectToArray($object); + $this->expr->objectToArray(...func_get_args()); return $this; } - /** - * Raises a number to the specified exponent and returns the result. - * - * The expression can be any valid expression as long as it - * resolves to a non-negative number. - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/pow/ - * @see Expr::pow - * - * @param mixed|Expr $number - * @param mixed|Expr $exponent - * - * @return static - */ - public function pow($number, $exponent): self + public function or($expression, ...$expressions): static { - $this->expr->pow($number, $exponent); + $this->expr->or(...func_get_args()); return $this; } - /** - * Returns an array whose elements are a generated sequence of numbers. - * - * $range generates the sequence from the specified starting number by successively incrementing the starting number by the specified step value up to but not including the end point. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/range/ - * @see Expr::range - * - * @param mixed|Expr $start An integer that specifies the start of the sequence. Can be any valid expression that resolves to an integer. - * @param mixed|Expr $end An integer that specifies the exclusive upper limit of the sequence. Can be any valid expression that resolves to an integer. - * @param mixed|Expr $step Optional. An integer that specifies the increment value. Can be any valid expression that resolves to a non-zero integer. Defaults to 1. - * - * @return static - */ - public function range($start, $end, $step = 1): self + public function pow($number, $exponent): static { - $this->expr->range($start, $end, $step); + $this->expr->pow(...func_get_args()); return $this; } - /** - * Applies an expression to each element in an array and combines them into - * a single value. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/reduce/ - * @see Expr::reduce - * - * @param mixed|Expr $input Can be any valid expression that resolves to an array. - * @param mixed|Expr $initialValue The initial cumulative value set before in is applied to the first element of the input array. - * @param mixed|Expr $in A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. - * - * @return static - */ - public function reduce($input, $initialValue, $in): self + public function range($start, $end, $step = null): static { - $this->expr->reduce($input, $initialValue, $in); + $this->expr->range(...func_get_args()); return $this; } - /** - * Accepts an array expression as an argument and returns an array with the - * elements in reverse order. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/reverseArray/ - * @see Expr::reverseArray - * - * @param mixed|Expr $expression - * - * @return static - */ - public function reverseArray($expression): self + public function reduce($input, $initialValue, $in): static { - $this->expr->reverseArray($expression); + $this->expr->reduce(...func_get_args()); return $this; } - /** - * Removes whitespace characters, including null, or the specified characters from the end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/rtrim/ - * - * @param mixed|Expr $input - * @param mixed|Expr $chars - */ - public function rtrim($input, $chars = null): self + public function regexFind($input, $regex, $options = null): static { - $this->expr->rtrim($input, $chars); + $this->expr->regexFind(...func_get_args()); return $this; } - /** - * Rounds a number to a whole integer or to a specified decimal place. - * - * The argument can be any valid expression as long as it resolves - * to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/round/ - * - * @param mixed|Expr $number - * @param mixed|Expr|null $place - */ - public function round($number, $place = null): self + public function regexFindAll($input, $regex, $options = null): static { - $this->expr->round($number, $place); + $this->expr->regexFindAll(...func_get_args()); return $this; } - /** - * Converts a value from radians to degrees. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/radiansToDegrees/ - * - * @param mixed|Expr $expression - */ - public function radiansToDegrees($expression): self + public function regexMatch($input, $regex, $options = null): static { - $this->expr->radiansToDegrees($expression); + $this->expr->regexMatch(...func_get_args()); return $this; } - /** - * Returns the second portion of a date as a number between 0 and 59, but - * can be 60 to account for leap seconds. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/second/ - * @see Expr::second - * - * @param mixed|Expr $expression - * - * @return static - */ - public function second($expression): self + public function replaceAll($input, $find, $replacement): static { - $this->expr->second($expression); + $this->expr->replaceAll(...func_get_args()); return $this; } - /** - * Takes two sets and returns an array containing the elements that only - * exist in the first set. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setDifference/ - * @see Expr::setDifference - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function setDifference($expression1, $expression2): self + public function replaceOne($input, $find, $replacement): static { - $this->expr->setDifference($expression1, $expression2); + $this->expr->replaceOne(...func_get_args()); return $this; } - /** - * Compares two or more arrays and returns true if they have the same - * distinct elements and false otherwise. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setEquals/ - * @see Expr::setEquals - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional sets - * - * @return static - */ - public function setEquals($expression1, $expression2, ...$expressions): self + public function reverseArray($expression): static + { + $this->expr->reverseArray(...func_get_args()); + + return $this; + } + + public function rtrim($input, $chars = null): static + { + $this->expr->rtrim(...func_get_args()); + + return $this; + } + + public function round($number, $place = null): static + { + $this->expr->round(...func_get_args()); + + return $this; + } + + public function radiansToDegrees($expression): static + { + $this->expr->radiansToDegrees(...func_get_args()); + + return $this; + } + + public function rand(): static + { + $this->expr->rand(); + + return $this; + } + + public function sampleRate(float $rate): static + { + $this->expr->sampleRate(...func_get_args()); + + return $this; + } + + public function second($expression): static + { + $this->expr->second(...func_get_args()); + + return $this; + } + + public function setDifference($expression1, $expression2): static + { + $this->expr->setDifference(...func_get_args()); + + return $this; + } + + public function setEquals($expression1, $expression2, ...$expressions): static { $this->expr->setEquals(...func_get_args()); return $this; } - /** - * Takes two or more arrays and returns an array that contains the elements - * that appear in every input array. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setIntersection/ - * @see Expr::setIntersection - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional sets - * - * @return static - */ - public function setIntersection($expression1, $expression2, ...$expressions): self + public function setField($field, $input, $value): static + { + $this->expr->setField(...func_get_args()); + + return $this; + } + + public function setIntersection($expression1, $expression2, ...$expressions): static { $this->expr->setIntersection(...func_get_args()); return $this; } - /** - * Takes two arrays and returns true when the first array is a subset of the - * second, including when the first array equals the second array, and false - * otherwise. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setIsSubset/ - * @see Expr::setIsSubset - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function setIsSubset($expression1, $expression2): self + public function setIsSubset($expression1, $expression2): static { - $this->expr->setIsSubset($expression1, $expression2); + $this->expr->setIsSubset(...func_get_args()); return $this; } - /** - * Takes two or more arrays and returns an array containing the elements - * that appear in any input array. - * - * The arguments can be any valid expression as long as they each resolve to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/setUnion/ - * @see Expr::setUnion - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * @param mixed|Expr ...$expressions Additional sets - * - * @return static - */ - public function setUnion($expression1, $expression2, ...$expressions): self + public function setUnion($expression1, $expression2, ...$expressions): static { $this->expr->setUnion(...func_get_args()); return $this; } - /** - * Returns the sine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sin/ - * - * @param mixed|Expr $expression - */ - public function sin($expression): self + public function sin($expression): static { - $this->expr->sin($expression); + $this->expr->sin(...func_get_args()); return $this; } - /** - * Returns the hyperbolic sine of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sinh/ - * - * @param mixed|Expr $expression - */ - public function sinh($expression): self + public function sinh($expression): static { - $this->expr->sinh($expression); + $this->expr->sinh(...func_get_args()); return $this; } - /** - * Counts and returns the total the number of items in an array. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/size/ - * @see Expr::size - * - * @param mixed|Expr $expression - * - * @return static - */ - public function size($expression): self + public function size($expression): static { - $this->expr->size($expression); + $this->expr->size(...func_get_args()); return $this; } - /** - * Returns a subset of an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/slice/ - * @see Expr::slice - * - * @param mixed|Expr $array - * @param mixed|Expr $n - * @param mixed|Expr|null $position - * - * @return static - */ - public function slice($array, $n, $position = null): self + public function slice($array, $n, $position = null): static { - $this->expr->slice($array, $n, $position); + $this->expr->slice(...func_get_args()); return $this; } - /** - * Divides a string into an array of substrings based on a delimiter. - * - * $split removes the delimiter and returns the resulting substrings as - * elements of an array. If the delimiter is not found in the string, $split - * returns the original string as the only element of an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/split/ - * - * @param mixed|Expr $string The string to be split. Can be any valid expression as long as it resolves to a string. - * @param mixed|Expr $delimiter The delimiter to use when splitting the string expression. Can be any valid expression as long as it resolves to a string. - * - * @return static - */ - public function split($string, $delimiter): self + public function sortArray($input, $sortBy): static { - $this->expr->split($string, $delimiter); + $this->expr->sortArray(...func_get_args()); return $this; } - /** - * Calculates the square root of a positive number and returns the result as - * a double. - * - * The argument can be any valid expression as long as it resolves to a - * non-negative number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sqrt/ - * @see Expr::sqrt - * - * @param mixed|Expr $expression - * - * @return static - */ - public function sqrt($expression): self + public function split($string, $delimiter): static { - $this->expr->sqrt($expression); + $this->expr->split(...func_get_args()); return $this; } - /** - * Performs case-insensitive comparison of two strings. Returns - * 1 if first string is “greater than” the second string. - * 0 if the two strings are equal. - * -1 if the first string is “less than” the second string. - * - * The arguments can be any valid expression as long as they resolve to strings. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strcasecmp/ - * @see Expr::strcasecmp - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function strcasecmp($expression1, $expression2): self + public function sqrt($expression): static { - $this->expr->strcasecmp($expression1, $expression2); + $this->expr->sqrt(...func_get_args()); return $this; } - /** - * Returns the number of UTF-8 encoded bytes in the specified string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strLenBytes/ - * - * @param mixed|Expr $string - * - * @return static - */ - public function strLenBytes($string): self + public function stdDevPop($expression, ...$expressions): static { - $this->expr->strLenBytes($string); + $this->expr->stdDevPop(...func_get_args()); return $this; } - /** - * Returns the number of UTF-8 code points in the specified string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/strLenCP/ - * - * @param mixed|Expr $string - * - * @return static - */ - public function strLenCP($string): self + public function stdDevSamp($expression, ...$expressions): static { - $this->expr->strLenCP($string); + $this->expr->stdDevSamp(...func_get_args()); return $this; } - /** - * Returns a substring of a string, starting at a specified index position - * and including the specified number of characters. The index is zero-based. - * - * The arguments can be any valid expression as long as long as the first argument resolves to a string, and the second and third arguments resolve to integers. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substr/ - * @see Expr::substr - * - * @param mixed|Expr $string - * @param mixed|Expr $start - * @param mixed|Expr $length - * - * @return static - */ - public function substr($string, $start, $length): self + public function strcasecmp($expression1, $expression2): static { - $this->expr->substr($string, $start, $length); + $this->expr->strcasecmp(...func_get_args()); return $this; } - /** - * Returns the substring of a string. - * - * The substring starts with the character at the specified UTF-8 byte index - * (zero-based) in the string and continues for the number of bytes - * specified. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substrBytes/ - * - * @param mixed|Expr $string The string from which the substring will be extracted. Can be any valid expression as long as it resolves to a string. - * @param mixed|Expr $start Indicates the starting point of the substring. Can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer. - * @param mixed|Expr $count Can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer. - * - * @return static - */ - public function substrBytes($string, $start, $count): self + public function strLenBytes($string): static { - $this->expr->substrBytes($string, $start, $count); + $this->expr->strLenBytes(...func_get_args()); return $this; } - /** - * Returns the substring of a string. - * - * The substring starts with the character at the specified UTF-8 code point - * (CP) index (zero-based) in the string for the number of code points - * specified. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/substrBytes/ - * - * @param mixed|Expr $string The string from which the substring will be extracted. Can be any valid expression as long as it resolves to a string. - * @param mixed|Expr $start Indicates the starting point of the substring. Can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer. - * @param mixed|Expr $count Can be any valid expression as long as it resolves to a non-negative integer or number that can be represented as an integer. - * - * @return static - */ - public function substrCP($string, $start, $count): self + public function strLenCP($string): static { - $this->expr->substrCP($string, $start, $count); + $this->expr->strLenCP(...func_get_args()); return $this; } - /** - * Subtracts two numbers to return the difference. The second argument is - * subtracted from the first argument. - * - * The arguments can be any valid expression as long as they resolve to numbers and/or dates. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/subtract/ - * @see Expr::subtract - * - * @param mixed|Expr $expression1 - * @param mixed|Expr $expression2 - * - * @return static - */ - public function subtract($expression1, $expression2): self + public function substr($string, $start, $length): static { - $this->expr->subtract($expression1, $expression2); + $this->expr->substr(...func_get_args()); return $this; } - /** - * Evaluates a series of case expressions. When it finds an expression which - * evaluates to true, $switch executes a specified expression and breaks out - * of the control flow. - * - * To add statements, use the {@link case()}, {@link then()} and - * {@link default()} methods. - */ - public function switch(): self + public function substrBytes($string, $start, $count): static + { + $this->expr->substrBytes(...func_get_args()); + + return $this; + } + + public function substrCP($string, $start, $count): static + { + $this->expr->substrCP(...func_get_args()); + + return $this; + } + + public function subtract($expression1, $expression2): static + { + $this->expr->subtract(...func_get_args()); + + return $this; + } + + public function sum($expression, ...$expressions): static + { + $this->expr->sum(...func_get_args()); + + return $this; + } + + public function switch(): static { $this->expr->switch(); return $this; } - /** - * Returns the tangent of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/tan/ - * - * @param mixed|Expr $expression - */ - public function tan($expression): self + public function tan($expression): static { - $this->expr->tan($expression); + $this->expr->tan(...func_get_args()); return $this; } - /** - * Returns the hyperbolic tangent of a value that is measured in radians. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/tanh/ - * - * @param mixed|Expr $expression - */ - public function tanh($expression): self + public function tanh($expression): static { - $this->expr->tanh($expression); + $this->expr->tanh(...func_get_args()); return $this; } - /** - * Adds a case statement for the current branch of the $switch operator. - * - * Requires {@link case()} to be called first. The argument can be any valid - * expression. - * - * @param mixed|Expr $expression - */ - public function then($expression): self + public function then($expression): static { - $this->expr->then($expression); + $this->expr->then(...func_get_args()); return $this; } - /** - * Converts value to a boolean. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toBool/ - * - * @param mixed|Expr $expression - */ - public function toBool($expression): self + public function toBool($expression): static { - $this->expr->toBool($expression); + $this->expr->toBool(...func_get_args()); return $this; } - /** - * Converts value to a Date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDate/ - * - * @param mixed|Expr $expression - */ - public function toDate($expression): self + public function toDate($expression): static { - $this->expr->toDate($expression); + $this->expr->toDate(...func_get_args()); return $this; } - /** - * Converts value to a Decimal128. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDecimal/ - * - * @param mixed|Expr $expression - */ - public function toDecimal($expression): self + public function toDecimal($expression): static { - $this->expr->toDecimal($expression); + $this->expr->toDecimal(...func_get_args()); return $this; } - /** - * Converts value to a double. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toDouble/ - * - * @param mixed|Expr $expression - */ - public function toDouble($expression): self + public function toDouble($expression): static { - $this->expr->toDouble($expression); + $this->expr->toDouble(...func_get_args()); return $this; } - /** - * Converts value to an integer. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toInt/ - * - * @param mixed|Expr $expression - */ - public function toInt($expression): self + public function toInt($expression): static { - $this->expr->toInt($expression); + $this->expr->toInt(...func_get_args()); return $this; } - /** - * Converts value to a long. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toLong/ - * - * @param mixed|Expr $expression - */ - public function toLong($expression): self + public function toLong($expression): static { - $this->expr->toLong($expression); + $this->expr->toLong(...func_get_args()); return $this; } - /** - * Converts value to an ObjectId. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toObjectId/ - * - * @param mixed|Expr $expression - */ - public function toObjectId($expression): self + public function toObjectId($expression): static { - $this->expr->toObjectId($expression); + $this->expr->toObjectId(...func_get_args()); return $this; } - /** - * Converts value to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toString/ - * - * @param mixed|Expr $expression - */ - public function toString($expression): self + public function toString($expression): static { - $this->expr->toString($expression); + $this->expr->toString(...func_get_args()); return $this; } - /** - * Converts a string to lowercase, returning the result. - * - * The argument can be any expression as long as it resolves to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toLower/ - * @see Expr::toLower - * - * @param mixed|Expr $expression - * - * @return static - */ - public function toLower($expression): self + public function toLower($expression): static { - $this->expr->toLower($expression); + $this->expr->toLower(...func_get_args()); return $this; } - /** - * Converts a string to uppercase, returning the result. - * - * The argument can be any expression as long as it resolves to a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/toUpper/ - * @see Expr::toUpper - * - * @param mixed|Expr $expression - * - * @return static - */ - public function toUpper($expression): self + public function toUpper($expression): static { - $this->expr->toUpper($expression); + $this->expr->toUpper(...func_get_args()); return $this; } - /** - * Removes whitespace characters, including null, or the specified characters from - * the beginning and end of a string. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/trim/ - * - * @param mixed|Expr $input - * @param mixed|Expr|null $chars - */ - public function trim($input, $chars = null): self + public function trim($input, $chars = null): static { - $this->expr->trim($input, $chars); + $this->expr->trim(...func_get_args()); return $this; } - /** - * Truncates a number to its integer. - * - * The expression can be any valid expression as long as it - * resolves to a number. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/trunc/ - * @see Expr::trunc - * - * @param mixed|Expr $number - * - * @return static - */ - public function trunc($number): self + public function trunc($number): static { - $this->expr->trunc($number); + $this->expr->trunc(...func_get_args()); return $this; } - /** - * Returns a string that specifies the BSON type of the argument. - * - * The argument can be any valid expression. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/type/ - * - * @param mixed|Expr $expression - * - * @return static - */ - public function type($expression): self + public function tsIncrement($expression): static { - $this->expr->type($expression); + $this->expr->tsIncrement(...func_get_args()); return $this; } - /** - * Returns the week of the year for a date as a number between 0 and 53. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/week/ - * @see Expr::week - * - * @param mixed|Expr $expression - * - * @return static - */ - public function week($expression): self + public function tsSecond($expression): static { - $this->expr->week($expression); + $this->expr->tsSecond(...func_get_args()); return $this; } - /** - * Returns the year portion of a date. - * - * The argument can be any expression as long as it resolves to a date. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/year/ - * @see Expr::year - * - * @param mixed|Expr $expression - * - * @return static - */ - public function year($expression): self + public function type($expression): static { - $this->expr->year($expression); + $this->expr->type(...func_get_args()); return $this; } - /** - * Transposes an array of input arrays so that the first element of the - * output array would be an array containing, the first element of the first - * input array, the first element of the second input array, etc. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/zip/ - * @see Expr::zip - * - * @param mixed|Expr $inputs An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. - * @param bool|null $useLongestLength A boolean which specifies whether the length of the longest array determines the number of arrays in the output array. - * @param mixed|Expr|null $defaults An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. - * - * @return static - */ - public function zip($inputs, ?bool $useLongestLength = null, $defaults = null): self + public function week($expression): static + { + $this->expr->week(...func_get_args()); + + return $this; + } + + public function year($expression): static + { + $this->expr->year(...func_get_args()); + + return $this; + } + + public function zip($inputs, ?bool $useLongestLength = null, $defaults = null): static { - $this->expr->zip($inputs, $useLongestLength, $defaults); + $this->expr->zip(...func_get_args()); return $this; } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php index 05d3dff5eb..5343cfeade 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php @@ -11,33 +11,50 @@ use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\Persistence\Mapping\MappingException as BaseMappingException; +use function is_array; + +/** + * @psalm-import-type OutputCollection from Merge + * @psalm-type OutStageExpression = array{'$out': OutputCollection} + */ class Out extends Stage { - private DocumentManager $dm; - - private string $collection; + /** + * @var array|string + * @psalm-var OutputCollection + */ + private $out; - public function __construct(Builder $builder, string $collection, DocumentManager $documentManager) + public function __construct(Builder $builder, string $collection, private DocumentManager $dm) { parent::__construct($builder); - $this->dm = $documentManager; $this->out($collection); } public function getExpression(): array { return [ - '$out' => $this->collection, + '$out' => $this->out, ]; } - public function out(string $collection): Stage\Out + /** + * @param string|array $collection + * @psalm-param OutputCollection $collection + */ + public function out($collection): Stage\Out { + if (is_array($collection)) { + $this->out = $collection; + + return $this; + } + try { $class = $this->dm->getClassMetadata($collection); - } catch (BaseMappingException $e) { - $this->collection = $collection; + } catch (BaseMappingException) { + $this->out = $collection; return $this; } @@ -53,6 +70,6 @@ private function fromDocument(ClassMetadata $classMetadata): void throw MappingException::cannotUseShardedCollectionInOutStage($classMetadata->name); } - $this->collection = $classMetadata->getCollection(); + $this->out = $classMetadata->getCollection(); } } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Project.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Project.php index 08e997c53f..c108e5f3e4 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Project.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Project.php @@ -6,13 +6,15 @@ use Doctrine\ODM\MongoDB\Aggregation\Expr; -use function func_get_args; - /** * Fluent interface for adding a $project stage to an aggregation pipeline. + * + * @psalm-import-type OperatorExpression from Expr + * @psalm-type ProjectStageExpression = array{'$project': array} */ class Project extends Operator { + /** @psalm-return ProjectStageExpression */ public function getExpression(): array { return [ @@ -20,30 +22,12 @@ public function getExpression(): array ]; } - /** - * Returns the average value of the numeric values that result from applying - * a specified expression to each document in a group of documents that - * share the same group by key. Ignores nun-numeric values. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/avg/ - * @see Expr::avg - * - * @param mixed|Expr $expression1 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function avg($expression1, ...$expressions): self - { - $this->expr->avg(empty($expressions) ? $expression1 : func_get_args()); - - return $this; - } - /** * Shorthand method to define which fields to be included. * * @param string[] $fields */ - public function includeFields(array $fields): self + public function includeFields(array $fields): static { foreach ($fields as $fieldName) { $this->field($fieldName)->expression(true); @@ -60,7 +44,7 @@ public function includeFields(array $fields): self * * @param string[] $fields */ - public function excludeFields(array $fields): self + public function excludeFields(array $fields): static { foreach ($fields as $fieldName) { $this->field($fieldName)->expression(false); @@ -68,92 +52,4 @@ public function excludeFields(array $fields): self return $this; } - - /** - * Returns the highest value that results from applying an expression to - * each document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/max/ - * @see Expr::max - * - * @param mixed|Expr $expression1 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function max($expression1, ...$expressions): self - { - $this->expr->max(empty($expressions) ? $expression1 : func_get_args()); - - return $this; - } - - /** - * Returns the lowest value that results from applying an expression to each - * document in a group of documents that share the same group by key. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/min/ - * @see Expr::min - * - * @param mixed|Expr $expression1 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function min($expression1, ...$expressions): self - { - $this->expr->min(empty($expressions) ? $expression1 : func_get_args()); - - return $this; - } - - /** - * Calculates the population standard deviation of the input values. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevPop/ - * @see Expr::stdDevPop - * - * @param mixed|Expr $expression1 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function stdDevPop($expression1, ...$expressions): self - { - $this->expr->stdDevPop(empty($expressions) ? $expression1 : func_get_args()); - - return $this; - } - - /** - * Calculates the sample standard deviation of the input values. - * - * The argument can be any expression as long as it resolves to an array. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/stdDevSamp/ - * @see Expr::stdDevSamp - * - * @param mixed|Expr $expression1 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function stdDevSamp($expression1, ...$expressions): self - { - $this->expr->stdDevSamp(empty($expressions) ? $expression1 : func_get_args()); - - return $this; - } - - /** - * Calculates and returns the sum of all the numeric values that result from - * applying a specified expression to each document in a group of documents - * that share the same group by key. Ignores nun-numeric values. - * - * @see https://docs.mongodb.com/manual/reference/operator/aggregation/sum/ - * @see Expr::sum - * - * @param mixed|Expr $expression1 - * @param mixed|Expr ...$expressions Additional expressions - */ - public function sum($expression1, ...$expressions): self - { - $this->expr->sum(empty($expressions) ? $expression1 : func_get_args()); - - return $this; - } } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Redact.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Redact.php index b05dca5baa..2ac792bdba 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Redact.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Redact.php @@ -4,8 +4,13 @@ namespace Doctrine\ODM\MongoDB\Aggregation\Stage; +use Doctrine\ODM\MongoDB\Aggregation\Expr; + /** * Fluent interface for adding a $redact stage to an aggregation pipeline. + * + * @psalm-import-type OperatorExpression from Expr + * @psalm-type SetStageExpression = array{'$redact': array} */ class Redact extends Operator { diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceRoot.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceRoot.php index e27598a386..3d25a2a74a 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceRoot.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceRoot.php @@ -4,68 +4,25 @@ namespace Doctrine\ODM\MongoDB\Aggregation\Stage; -use Doctrine\ODM\MongoDB\Aggregation\Builder; use Doctrine\ODM\MongoDB\Aggregation\Expr; -use Doctrine\ODM\MongoDB\DocumentManager; -use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; -use Doctrine\ODM\MongoDB\Persisters\DocumentPersister; -use Doctrine\ODM\MongoDB\Types\Type; -use function array_map; -use function is_array; -use function is_string; -use function substr; - -class ReplaceRoot extends Operator +/** + * @psalm-import-type OperatorExpression from Expr + * @psalm-type ReplaceRootStageExpression = array{ + * '$replaceRoot': array{ + * newRoot: OperatorExpression|string, + * } + * } + */ +class ReplaceRoot extends AbstractReplace { - private DocumentManager $dm; - - private ClassMetadata $class; - - /** @var string|mixed[]|Expr|null */ - private $expression; - - /** @param string|mixed[]|Expr|null $expression */ - public function __construct(Builder $builder, DocumentManager $documentManager, ClassMetadata $class, $expression = null) - { - parent::__construct($builder); - - $this->dm = $documentManager; - $this->class = $class; - $this->expression = $expression; - } - + /** @return ReplaceRootStageExpression */ public function getExpression(): array { - $expression = $this->expression !== null ? $this->convertExpression($this->expression) : $this->expr->getExpression(); - return [ '$replaceRoot' => [ - 'newRoot' => is_array($expression) ? (object) $expression : $expression, + 'newRoot' => $this->getReplaceExpression(), ], ]; } - - /** - * @param mixed[]|string|mixed $expression - * - * @return mixed[]|string|mixed - */ - private function convertExpression($expression) - { - if (is_array($expression)) { - return array_map([$this, 'convertExpression'], $expression); - } - - if (is_string($expression) && substr($expression, 0, 1) === '$') { - return '$' . $this->getDocumentPersister()->prepareFieldName(substr($expression, 1)); - } - - return Type::convertPHPToDatabaseValue(Expr::convertExpression($expression)); - } - - private function getDocumentPersister(): DocumentPersister - { - return $this->dm->getUnitOfWork()->getDocumentPersister($this->class->name); - } } diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceWith.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceWith.php new file mode 100644 index 0000000000..317cde3c28 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/ReplaceWith.php @@ -0,0 +1,20 @@ + $this->getReplaceExpression()]; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sample.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sample.php index a60bc0b514..24c8d6b275 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sample.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sample.php @@ -9,18 +9,17 @@ /** * Fluent interface for adding a $sample stage to an aggregation pipeline. + * + * @psalm-type SampleStageExpression = array{'$sample': array{size: int}} */ class Sample extends Stage { - private int $size; - - public function __construct(Builder $builder, int $size) + public function __construct(Builder $builder, private int $size) { parent::__construct($builder); - - $this->size = $size; } + /** @psalm-return SampleStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php new file mode 100644 index 0000000000..e64de19729 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php @@ -0,0 +1,147 @@ +indexName) { + $params->index = $this->indexName; + } + + if ($this->count) { + $params->count = $this->count; + } + + if ($this->highlight) { + $params->highlight = $this->highlight; + } + + if ($this->returnStoredSource !== null) { + $params->returnStoredSource = $this->returnStoredSource; + } + + if ($this->operator !== null) { + $operatorName = $this->operator->getOperatorName(); + $params->$operatorName = $this->operator->getOperatorParams(); + } + + return ['$search' => $params]; + } + + public function index(string $name): static + { + $this->indexName = $name; + + return $this; + } + + /** @psalm-param CountType $type */ + public function countDocuments(string $type, ?int $threshold = null): static + { + $this->count = (object) ['type' => $type]; + + if ($threshold !== null) { + $this->count->threshold = $threshold; + } + + return $this; + } + + public function highlight(string $path, ?int $maxCharsToExamine = null, ?int $maxNumPassages = null): static + { + $this->highlight = (object) ['path' => $path]; + + if ($maxCharsToExamine !== null) { + $this->highlight->maxCharsToExamine = $maxCharsToExamine; + } + + if ($maxNumPassages !== null) { + $this->highlight->maxNumPassages = $maxNumPassages; + } + + return $this; + } + + public function returnStoredSource(bool $returnStoredSource = true): static + { + $this->returnStoredSource = $returnStoredSource; + + return $this; + } + + /** + * @param T $operator + * + * @return T + * + * @template T of SearchOperator + */ + protected function addOperator(SearchOperator $operator): SearchOperator + { + return $this->operator = $operator; + } + + protected function getSearchStage(): static + { + return $this; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/AbstractSearchOperator.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/AbstractSearchOperator.php new file mode 100644 index 0000000000..54e3945b43 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/AbstractSearchOperator.php @@ -0,0 +1,48 @@ +builder); + } + + public function index(string $name): Search + { + return $this->search->index($name); + } + + public function countDocuments(string $type, ?int $threshold = null): Search + { + return $this->search->countDocuments($type, $threshold); + } + + public function highlight(string $path, ?int $maxCharsToExamine = null, ?int $maxNumPassages = null): Search + { + return $this->search->highlight($path, $maxCharsToExamine, $maxNumPassages); + } + + public function returnStoredSource(bool $returnStoredSource): Search + { + return $this->search->returnStoredSource($returnStoredSource); + } + + /** @return array */ + final public function getExpression(): array + { + return [$this->getOperatorName() => $this->getOperatorParams()]; + } + + protected function getSearchStage(): Search + { + return $this->search; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Autocomplete.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Autocomplete.php new file mode 100644 index 0000000000..bb0d8fcace --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Autocomplete.php @@ -0,0 +1,95 @@ + */ + private array $query; + private string $path; + private string $tokenOrder = ''; + private ?object $fuzzy = null; + + public function __construct(Search $search, string $path, string ...$query) + { + parent::__construct($search); + + $this->query(...$query); + $this->path($path); + } + + public function query(string ...$query): static + { + $this->query = array_values($query); + + return $this; + } + + public function path(string $path): static + { + $this->path = $path; + + return $this; + } + + public function tokenOrder(string $order): static + { + $this->tokenOrder = $order; + + return $this; + } + + public function fuzzy(?int $maxEdits = null, ?int $prefixLength = null, ?int $maxExpansions = null): static + { + $this->fuzzy = (object) []; + if ($maxEdits !== null) { + $this->fuzzy->maxEdits = $maxEdits; + } + + if ($prefixLength !== null) { + $this->fuzzy->prefixLength = $prefixLength; + } + + if ($maxExpansions !== null) { + $this->fuzzy->maxExpansions = $maxExpansions; + } + + return $this; + } + + public function getOperatorName(): string + { + return 'autocomplete'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'query' => $this->query, + 'path' => $this->path, + ]; + + if ($this->tokenOrder) { + $params->tokenOrder = $this->tokenOrder; + } + + if ($this->fuzzy) { + $params->fuzzy = $this->fuzzy; + } + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound.php new file mode 100644 index 0000000000..8b8f604276 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound.php @@ -0,0 +1,125 @@ +> */ + private array $operators = [ + 'must' => [], + 'mustNot' => [], + 'should' => [], + 'filter' => [], + ]; + + private string $currentClause = 'must'; + private ?int $minimumShouldMatch = null; + + /** + * @param T $operator + * + * @return T + * + * @template T of SearchOperator + */ + protected function addOperator(SearchOperator $operator): SearchOperator + { + $this->operators[$this->currentClause][] = $operator; + + return $operator; + } + + public function must(): static + { + $this->currentClause = 'must'; + + return $this; + } + + public function mustNot(): static + { + $this->currentClause = 'mustNot'; + + return $this; + } + + public function should(?int $minimumShouldMatch = null): static + { + $this->currentClause = 'should'; + + if ($minimumShouldMatch !== null) { + $this->minimumShouldMatch($minimumShouldMatch); + } + + return $this; + } + + public function filter(): static + { + $this->currentClause = 'filter'; + + return $this; + } + + public function minimumShouldMatch(int $minimumShouldMatch): static + { + $this->minimumShouldMatch = $minimumShouldMatch; + + return $this; + } + + public function getOperatorName(): string + { + return 'compound'; + } + + public function getOperatorParams(): object + { + $params = (object) []; + + foreach ($this->operators as $clause => $operators) { + if (! $operators) { + continue; + } + + $params->$clause = array_map( + static function (SearchOperator $operator): object { + return (object) [ + $operator->getOperatorName() => $operator->getOperatorParams(), + ]; + }, + $operators, + ); + } + + if ($this->minimumShouldMatch !== null) { + $params->minimumShouldMatch = $this->minimumShouldMatch; + } + + return $this->appendScore($params); + } + + protected function getAddOperatorClosure(): Closure + { + return Closure::fromCallable([$this, 'addOperator']); + } + + protected function getCompoundStage(): Compound + { + return $this; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedAutocomplete.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedAutocomplete.php new file mode 100644 index 0000000000..d15814fd4a --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedAutocomplete.php @@ -0,0 +1,17 @@ +compound->must(); + } + + public function mustNot(): Compound + { + return $this->compound->mustNot(); + } + + public function should(?int $minimumShouldMatch = null): Compound + { + return $this->compound->should($minimumShouldMatch); + } + + public function filter(): Compound + { + return $this->compound->filter(); + } + + /** + * @param T $operator + * + * @return T + * + * @template T of SearchOperator + */ + protected function addOperator(SearchOperator $operator): SearchOperator + { + return $this->getAddOperatorClosure()($operator); + } + + protected function getAddOperatorClosure(): Closure + { + return $this->addOperator; + } + + protected function getCompoundStage(): Compound + { + return $this->compound; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/EmbeddedDocument.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/EmbeddedDocument.php new file mode 100644 index 0000000000..fe8d5a11b1 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/EmbeddedDocument.php @@ -0,0 +1,63 @@ +path($path); + } + + public function path(string $path): static + { + $this->path = $path; + + return $this; + } + + /** + * @param T $operator + * + * @return T + * + * @template T of SearchOperator + */ + protected function addOperator(SearchOperator $operator): SearchOperator + { + return $this->operator = $operator; + } + + public function getOperatorName(): string + { + return 'embeddedDocument'; + } + + public function getOperatorParams(): object + { + $params = (object) ['path' => $this->path]; + + if ($this->operator) { + $params->operator = (object) $this->operator->getExpression(); + } + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Equals.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Equals.php new file mode 100644 index 0000000000..960013dbe6 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Equals.php @@ -0,0 +1,64 @@ +path($path) + ->value($value); + } + + public function path(string $path): static + { + $this->path = $path; + + return $this; + } + + /** @param string|int|float|ObjectId|UTCDateTime|null $value */ + public function value($value): static + { + $this->value = $value; + + return $this; + } + + public function getOperatorName(): string + { + return 'equals'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'path' => $this->path, + 'value' => $this->value, + ]; + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Exists.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Exists.php new file mode 100644 index 0000000000..8ad546384c --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Exists.php @@ -0,0 +1,30 @@ + $this->path]; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoShape.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoShape.php new file mode 100644 index 0000000000..600ad2f936 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoShape.php @@ -0,0 +1,80 @@ + */ + private array $path = []; + private string $relation = ''; + + /** @var LineString|Point|Polygon|MultiPolygon|array|null */ + private $geometry = null; + + /** @param LineString|Point|Polygon|MultiPolygon|array|null $geometry */ + public function __construct(Search $search, $geometry = null, string $relation = '', string ...$path) + { + parent::__construct($search); + + $this + ->geometry($geometry) + ->relation($relation) + ->path(...$path); + } + + public function path(string ...$path): static + { + $this->path = $path; + + return $this; + } + + public function relation(string $relation): static + { + $this->relation = $relation; + + return $this; + } + + /** @param LineString|Point|Polygon|MultiPolygon|array|null $geometry */ + public function geometry($geometry): static + { + $this->geometry = $geometry; + + return $this; + } + + public function getOperatorName(): string + { + return 'geoShape'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'path' => $this->path, + 'relation' => $this->relation, + 'geometry' => $this->geometry instanceof Geometry + ? $this->geometry->jsonSerialize() + : $this->geometry, + ]; + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php new file mode 100644 index 0000000000..026b850cc0 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php @@ -0,0 +1,118 @@ + */ + private array $path = []; + private string $relation = ''; + private ?object $box = null; + private ?object $circle = null; + + /** @var array|MultiPolygon|Polygon|null */ + private $geometry = null; + + public function __construct(Search $search, string ...$path) + { + parent::__construct($search); + + $this->path(...$path); + } + + public function path(string ...$path): static + { + $this->path = $path; + + return $this; + } + + /** + * @param array|Point $bottomLeft + * @param array|Point $topRight + */ + public function box($bottomLeft, $topRight): static + { + $this->box = (object) [ + 'bottomLeft' => $this->convertGeometry($bottomLeft), + 'topRight' => $this->convertGeometry($topRight), + ]; + + return $this; + } + + /** + * @param array|Point $center + * @param int|float $radius + */ + public function circle($center, $radius): static + { + $this->circle = (object) [ + 'center' => $this->convertGeometry($center), + 'radius' => $radius, + ]; + + return $this; + } + + /** @param Polygon|MultiPolygon|array $geometry */ + public function geometry($geometry): static + { + $this->geometry = $geometry; + + return $this; + } + + public function getOperatorName(): string + { + return 'geoWithin'; + } + + public function getOperatorParams(): object + { + $params = (object) ['path' => $this->path]; + + if ($this->box) { + $params->box = $this->box; + } + + if ($this->circle) { + $params->circle = $this->circle; + } + + if ($this->geometry) { + $params->geometry = $this->convertGeometry($this->geometry); + } + + return $this->appendScore($params); + } + + /** + * @param array|Geometry $geometry + * + * @return array + */ + private function convertGeometry($geometry): array + { + if (! $geometry instanceof Geometry) { + return $geometry; + } + + return $geometry->jsonSerialize(); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/MoreLikeThis.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/MoreLikeThis.php new file mode 100644 index 0000000000..198a7f3413 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/MoreLikeThis.php @@ -0,0 +1,38 @@ +|object> */ + private array $like = []; + + /** @param array|object $documents */ + public function __construct(Search $search, ...$documents) + { + parent::__construct($search); + + $this->like = array_values($documents); + } + + public function getOperatorName(): string + { + return 'moreLikeThis'; + } + + public function getOperatorParams(): object + { + return (object) ['like' => $this->like]; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Near.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Near.php new file mode 100644 index 0000000000..ad0e1d1d3c --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Near.php @@ -0,0 +1,84 @@ + */ + private array $path; + + /** + * @param int|float|UTCDateTime|array|Point|null $origin + * @param int|float|null $pivot + */ + public function __construct(Search $search, $origin = null, $pivot = null, string ...$path) + { + parent::__construct($search); + + $this + ->origin($origin) + ->pivot($pivot) + ->path(...$path); + } + + /** @param int|float|UTCDateTime|array|Point|null $origin */ + public function origin($origin): static + { + $this->origin = $origin; + + return $this; + } + + /** @param int|float|null $pivot */ + public function pivot($pivot): static + { + $this->pivot = $pivot; + + return $this; + } + + public function path(string ...$path): static + { + $this->path = $path; + + return $this; + } + + public function getOperatorName(): string + { + return 'near'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'origin' => $this->origin instanceof Geometry + ? $this->origin->jsonSerialize() + : $this->origin, + 'pivot' => $this->pivot, + 'path' => $this->path, + ]; + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Phrase.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Phrase.php new file mode 100644 index 0000000000..e397003bc0 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Phrase.php @@ -0,0 +1,64 @@ + */ + private array $query = []; + + /** @var list */ + private array $path = []; + private ?int $slop = null; + + public function query(string ...$query): static + { + $this->query = array_values($query); + + return $this; + } + + public function path(string ...$path): static + { + $this->path = $path; + + return $this; + } + + public function slop(int $slop): static + { + $this->slop = $slop; + + return $this; + } + + public function getOperatorName(): string + { + return 'phrase'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'query' => $this->query, + 'path' => $this->path, + ]; + + if ($this->slop !== null) { + $params->slop = $this->slop; + } + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/QueryString.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/QueryString.php new file mode 100644 index 0000000000..6b00ce5ed7 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/QueryString.php @@ -0,0 +1,58 @@ +query($query) + ->defaultPath($defaultPath); + } + + public function query(string $query): static + { + $this->query = $query; + + return $this; + } + + public function defaultPath(string $defaultPath): static + { + $this->defaultPath = $defaultPath; + + return $this; + } + + public function getOperatorName(): string + { + return 'queryString'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'query' => $this->query, + 'defaultPath' => $this->defaultPath, + ]; + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Range.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Range.php new file mode 100644 index 0000000000..62bed26a8e --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Range.php @@ -0,0 +1,94 @@ + */ + private array $path; + + /** @param int|float|UTCDateTime|null $value */ + public function gt($value): static + { + $this->gt = $value; + $this->includeLowerBound = false; + + return $this; + } + + /** @param int|float|UTCDateTime|null $value */ + public function gte($value): static + { + $this->gt = $value; + $this->includeLowerBound = true; + + return $this; + } + + /** @param int|float|UTCDateTime|null $value */ + public function lt($value): static + { + $this->lt = $value; + $this->includeLowerBound = false; + + return $this; + } + + /** @param int|float|UTCDateTime|null $value */ + public function lte($value): static + { + $this->lt = $value; + $this->includeLowerBound = true; + + return $this; + } + + public function path(string ...$path): static + { + $this->path = $path; + + return $this; + } + + public function getOperatorName(): string + { + return 'range'; + } + + public function getOperatorParams(): object + { + $params = (object) ['path' => $this->path]; + + if ($this->gt !== null) { + $name = $this->includeLowerBound ? 'gte' : 'gt'; + $params->$name = $this->gt; + } + + if ($this->lt !== null) { + $name = $this->includeLowerBound ? 'lte' : 'lt'; + $params->$name = $this->lt; + } + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Regex.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Regex.php new file mode 100644 index 0000000000..90fd1f8d8d --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Regex.php @@ -0,0 +1,62 @@ + */ + private array $query = []; + + /** @var list */ + private array $path = []; + private ?bool $allowAnalyzedField = null; + + public function query(string ...$query): static + { + $this->query = $query; + + return $this; + } + + public function path(string ...$path): static + { + $this->path = $path; + + return $this; + } + + public function allowAnalyzedField(bool $allowAnalyzedField = true): static + { + $this->allowAnalyzedField = $allowAnalyzedField; + + return $this; + } + + public function getOperatorName(): string + { + return 'regex'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'query' => $this->query, + 'path' => $this->path, + ]; + + if ($this->allowAnalyzedField !== null) { + $params->allowAnalyzedField = $this->allowAnalyzedField; + } + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/ScoredSearchOperator.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/ScoredSearchOperator.php new file mode 100644 index 0000000000..46db863e5a --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/ScoredSearchOperator.php @@ -0,0 +1,12 @@ +score) { + return $params; + } + + $params->score = $this->score; + + return $params; + } + + public function boostScore(?float $value = null, ?string $path = null, ?float $undefined = null): static + { + $boost = (object) []; + if ($value !== null) { + $boost->value = $value; + } + + if ($path !== null) { + $boost->path = $path; + } + + if ($undefined !== null) { + $boost->undefined = $undefined; + } + + $this->score = (object) ['boost' => $boost]; + + return $this; + } + + public function constantScore(float $value): static + { + $this->score = (object) [ + 'constant' => (object) ['value' => $value], + ]; + + return $this; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SearchOperator.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SearchOperator.php new file mode 100644 index 0000000000..872b7cebcf --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SearchOperator.php @@ -0,0 +1,22 @@ + + */ + public function getExpression(): array; + + /** @internal */ + public function getOperatorName(): string; + + /** @internal */ + public function getOperatorParams(): object; +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsAllSearchOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsAllSearchOperators.php new file mode 100644 index 0000000000..a664adb148 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsAllSearchOperators.php @@ -0,0 +1,9 @@ +addOperator(new Autocomplete($this->getSearchStage(), $path, ...$query)); + } + + public function compound(): Compound + { + return $this->addOperator(new Compound($this->getSearchStage())); + } + + public function embeddedDocument(string $path = ''): EmbeddedDocument + { + return $this->addOperator(new EmbeddedDocument($this->getSearchStage(), $path)); + } + + /** @param string|int|float|ObjectId|UTCDateTime|null $value */ + public function equals(string $path = '', $value = null): Equals + { + return $this->addOperator(new Equals($this->getSearchStage(), $path, $value)); + } + + public function exists(string $path): Exists + { + return $this->addOperator(new Exists($this->getSearchStage(), $path)); + } + + /** @param LineString|Point|Polygon|MultiPolygon|array|null $geometry */ + public function geoShape($geometry = null, string $relation = '', string ...$path): GeoShape + { + return $this->addOperator(new GeoShape($this->getSearchStage(), $geometry, $relation, ...$path)); + } + + public function geoWithin(string ...$path): GeoWithin + { + return $this->addOperator(new GeoWithin($this->getSearchStage(), ...$path)); + } + + /** @param array|object $documents */ + public function moreLikeThis(...$documents): MoreLikeThis + { + return $this->addOperator(new MoreLikeThis($this->getSearchStage(), ...$documents)); + } + + /** + * @param int|float|UTCDateTime|array|Point|null $origin + * @param int|float|null $pivot + */ + public function near($origin = null, $pivot = null, string ...$path): Near + { + return $this->addOperator(new Near($this->getSearchStage(), $origin, $pivot, ...$path)); + } + + public function phrase(): Phrase + { + return $this->addOperator(new Phrase($this->getSearchStage())); + } + + public function queryString(string $query = '', string $defaultPath = ''): QueryString + { + return $this->addOperator(new QueryString($this->getSearchStage(), $query, $defaultPath)); + } + + public function range(): Range + { + return $this->addOperator(new Range($this->getSearchStage())); + } + + public function regex(): Regex + { + return $this->addOperator(new Regex($this->getSearchStage())); + } + + public function text(): Text + { + return $this->addOperator(new Text($this->getSearchStage())); + } + + public function wildcard(): Wildcard + { + return $this->addOperator(new Wildcard($this->getSearchStage())); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsAutocompleteOperator.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsAutocompleteOperator.php new file mode 100644 index 0000000000..360831475d --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsAutocompleteOperator.php @@ -0,0 +1,10 @@ +addOperator(new CompoundedAutocomplete($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), $path, ...$query)); + } + + /** @return EmbeddedDocument&CompoundSearchOperatorInterface */ + public function embeddedDocument(string $path = ''): EmbeddedDocument + { + return $this->addOperator(new CompoundedEmbeddedDocument($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), $path)); + } + + /** + * @param string|int|float|ObjectId|UTCDateTime|null $value + * + * @return Equals&CompoundSearchOperatorInterface + */ + public function equals(string $path = '', $value = null): Equals + { + return $this->addOperator(new CompoundedEquals($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), $path, $value)); + } + + /** @return Exists&CompoundSearchOperatorInterface */ + public function exists(string $path): Exists + { + return $this->addOperator(new CompoundedExists($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), $path)); + } + + /** + * @param LineString|Point|Polygon|MultiPolygon|array|null $geometry + * + * @return GeoShape&CompoundSearchOperatorInterface + */ + public function geoShape($geometry = null, string $relation = '', string ...$path): GeoShape + { + return $this->addOperator(new CompoundedGeoShape($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), $geometry, $relation, ...$path)); + } + + /** @return GeoWithin&CompoundSearchOperatorInterface */ + public function geoWithin(string ...$path): GeoWithin + { + return $this->addOperator(new CompoundedGeoWithin($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), ...$path)); + } + + /** + * @param array|object $documents + * + * @return MoreLikeThis&CompoundSearchOperatorInterface + */ + public function moreLikeThis(...$documents): MoreLikeThis + { + return $this->addOperator(new CompoundedMoreLikeThis($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), ...$documents)); + } + + /** + * @param int|float|UTCDateTime|array|Point|null $origin + * @param int|float|null $pivot + * + * @return Near&CompoundSearchOperatorInterface + */ + public function near($origin = null, $pivot = null, string ...$path): Near + { + return $this->addOperator(new CompoundedNear($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), $origin, $pivot, ...$path)); + } + + /** @return Phrase&CompoundSearchOperatorInterface */ + public function phrase(): Phrase + { + return $this->addOperator(new CompoundedPhrase($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage())); + } + + /** @return QueryString&CompoundSearchOperatorInterface */ + public function queryString(string $query = '', string $defaultPath = ''): QueryString + { + return $this->addOperator(new CompoundedQueryString($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage(), $query, $defaultPath)); + } + + /** @return Range&CompoundSearchOperatorInterface */ + public function range(): Range + { + return $this->addOperator(new CompoundedRange($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage())); + } + + /** @return Regex&CompoundSearchOperatorInterface */ + public function regex(): Regex + { + return $this->addOperator(new CompoundedRegex($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage())); + } + + /** @return Text&CompoundSearchOperatorInterface */ + public function text(): Text + { + return $this->addOperator(new CompoundedText($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage())); + } + + /** @return Wildcard&CompoundSearchOperatorInterface */ + public function wildcard(): Wildcard + { + return $this->addOperator(new CompoundedWildcard($this->getCompoundStage(), $this->getAddOperatorClosure(), $this->getSearchStage())); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsCompoundableSearchOperators.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsCompoundableSearchOperators.php new file mode 100644 index 0000000000..f1c2f097ac --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsCompoundableSearchOperators.php @@ -0,0 +1,9 @@ +|object $documents */ + public function moreLikeThis(...$documents): MoreLikeThis; +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsNearOperator.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsNearOperator.php new file mode 100644 index 0000000000..31fc8b477e --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsNearOperator.php @@ -0,0 +1,17 @@ + */ + private array $query = []; + + /** @var list */ + private array $path = []; + private ?object $fuzzy = null; + private string $synonyms = ''; + + public function query(string ...$query): static + { + $this->query = array_values($query); + + return $this; + } + + public function path(string ...$path): static + { + $this->path = $path; + + return $this; + } + + public function fuzzy(?int $maxEdits = null, ?int $prefixLength = null, ?int $maxExpansions = null): static + { + $this->fuzzy = (object) []; + if ($maxEdits !== null) { + $this->fuzzy->maxEdits = $maxEdits; + } + + if ($prefixLength !== null) { + $this->fuzzy->prefixLength = $prefixLength; + } + + if ($maxExpansions !== null) { + $this->fuzzy->maxExpansions = $maxExpansions; + } + + return $this; + } + + public function synonyms(string $synonyms): static + { + $this->synonyms = $synonyms; + + return $this; + } + + public function getOperatorName(): string + { + return 'text'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'query' => $this->query, + 'path' => $this->path, + ]; + + if ($this->fuzzy) { + $params->fuzzy = $this->fuzzy; + } + + if ($this->synonyms) { + $params->synonyms = $this->synonyms; + } + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Wildcard.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Wildcard.php new file mode 100644 index 0000000000..43257ff355 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Wildcard.php @@ -0,0 +1,62 @@ + */ + private array $query = []; + + /** @var list */ + private array $path = []; + private ?bool $allowAnalyzedField = null; + + public function query(string ...$query): static + { + $this->query = $query; + + return $this; + } + + public function path(string ...$path): static + { + $this->path = $path; + + return $this; + } + + public function allowAnalyzedField(bool $allowAnalyzedField = true): static + { + $this->allowAnalyzedField = $allowAnalyzedField; + + return $this; + } + + public function getOperatorName(): string + { + return 'wildcard'; + } + + public function getOperatorParams(): object + { + $params = (object) [ + 'query' => $this->query, + 'path' => $this->path, + ]; + + if ($this->allowAnalyzedField !== null) { + $params->allowAnalyzedField = $this->allowAnalyzedField; + } + + return $this->appendScore($params); + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Set.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Set.php new file mode 100644 index 0000000000..cca5aed345 --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Set.php @@ -0,0 +1,22 @@ +} + */ +final class Set extends Operator +{ + /** @psalm-return SetStageExpression */ + public function getExpression(): array + { + return ['$set' => $this->expr->getExpression()]; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Skip.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Skip.php index 062072dd44..a08fd1167c 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Skip.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Skip.php @@ -9,18 +9,17 @@ /** * Fluent interface for adding a $skip stage to an aggregation pipeline. + * + * @psalm-type SkipStageExpression = array{'$skip': int} */ class Skip extends Stage { - private int $skip; - - public function __construct(Builder $builder, int $skip) + public function __construct(Builder $builder, private int $skip) { parent::__construct($builder); - - $this->skip = $skip; } + /** @return SkipStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sort.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sort.php index e10bf3e062..235d753a6a 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sort.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Sort.php @@ -16,8 +16,12 @@ * Fluent interface for adding a $sort stage to an aggregation pipeline. * * @psalm-type SortMetaKeywords = 'textScore'|'indexKey' - * @psalm-type SortMeta = array{"$meta": SortMetaKeywords} - * @psalm-type SortShape = array + * @psalm-type SortDirectionKeywords = 'asc'|'desc' + * @psalm-type SortMeta = array{'$meta': SortMetaKeywords} + * @psalm-type SortShape = array + * @psalm-type SortStageExpression = array{ + * '$sort': array + * } */ class Sort extends Stage { @@ -27,7 +31,8 @@ class Sort extends Stage /** * @param array>|string $fieldName Field name or array of field/order pairs * @param int|string $order Field order (if one field is specified) - * @psalm-param SortShape|string $fieldName + * @psalm-param SortShape|string $fieldName + * @psalm-param int|SortMeta|SortDirectionKeywords|null $order */ public function __construct(Builder $builder, $fieldName, $order = null) { @@ -50,6 +55,7 @@ public function __construct(Builder $builder, $fieldName, $order = null) } } + /** @psalm-return SortStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SortByCount.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SortByCount.php index 737675d86d..e37fd46689 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SortByCount.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/SortByCount.php @@ -11,6 +11,7 @@ use function substr; +/** @psalm-type SortByCountStageExpression = array{'$sortByCount': string} */ class SortByCount extends Stage { private string $fieldName; @@ -20,14 +21,15 @@ class SortByCount extends Stage * prefix the field name with a dollar sign $ and enclose it in quotes. * The expression can not evaluate to an object. */ - public function __construct(Builder $builder, string $fieldName, DocumentManager $documentManager, ClassMetadata $class) + public function __construct(Builder $builder, string $fieldName, DocumentManager $dm, ClassMetadata $class) { parent::__construct($builder); - $documentPersister = $documentManager->getUnitOfWork()->getDocumentPersister($class->name); + $documentPersister = $dm->getUnitOfWork()->getDocumentPersister($class->name); $this->fieldName = '$' . $documentPersister->prepareFieldName(substr($fieldName, 1)); } + /** @psalm-return SortByCountStageExpression */ public function getExpression(): array { return [ diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php new file mode 100644 index 0000000000..8d651ef42e --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php @@ -0,0 +1,76 @@ +dm->getClassMetadata($collection); + $this->collection = $class->getCollection(); + } catch (MappingException) { + } + } + + /** + * @param array|Builder|Stage $pipeline + * @psalm-param PipelineParamType $pipeline + */ + public function pipeline($pipeline): static + { + if ($pipeline instanceof Stage) { + $this->pipeline = $pipeline->builder; + } else { + $this->pipeline = $pipeline; + } + + if ($this->builder === $this->pipeline) { + throw new InvalidArgumentException('Cannot use the same Builder instance for $unionWith pipeline.'); + } + + return $this; + } + + /** @psalm-return UnionWithStageExpression */ + public function getExpression(): array + { + $params = (object) ['coll' => $this->collection]; + + if ($this->pipeline) { + $params->pipeline = $this->pipeline instanceof Builder + ? $this->pipeline->getPipeline(false) + : $this->pipeline; + } + + return ['$unionWith' => $params]; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnsetStage.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnsetStage.php new file mode 100644 index 0000000000..c6e6bd7acb --- /dev/null +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnsetStage.php @@ -0,0 +1,38 @@ +} + */ +class UnsetStage extends Stage +{ + /** @var list */ + private array $fields; + + public function __construct(Builder $builder, private DocumentPersister $documentPersister, string ...$fields) + { + parent::__construct($builder); + + $this->fields = array_values($fields); + } + + /** @return UnsetStageExpression */ + public function getExpression(): array + { + return [ + '$unset' => array_map([$this->documentPersister, 'prepareFieldName'], $this->fields), + ]; + } +} diff --git a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Unwind.php b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Unwind.php index dd2ff3dc29..66596d398f 100644 --- a/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Unwind.php +++ b/lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Unwind.php @@ -9,22 +9,27 @@ /** * Fluent interface for adding a $unwind stage to an aggregation pipeline. + * + * @psalm-type UnwindStageExpression = array{ + * '$unwind': string|array{ + * path: string, + * includeArrayIndex?: string, + * preserveNullAndEmptyArrays?: bool, + * } + * } */ class Unwind extends Stage { - private string $fieldName; - private ?string $includeArrayIndex = null; private bool $preserveNullAndEmptyArrays = false; - public function __construct(Builder $builder, string $fieldName) + public function __construct(Builder $builder, private string $fieldName) { parent::__construct($builder); - - $this->fieldName = $fieldName; } + /** @psalm-return UnwindStageExpression */ public function getExpression(): array { // Fallback behavior for MongoDB < 3.2 @@ -36,12 +41,12 @@ public function getExpression(): array $unwind = ['path' => $this->fieldName]; - foreach (['includeArrayIndex', 'preserveNullAndEmptyArrays'] as $option) { - if (! $this->$option) { - continue; - } + if ($this->includeArrayIndex) { + $unwind['includeArrayIndex'] = $this->includeArrayIndex; + } - $unwind[$option] = $this->$option; + if ($this->preserveNullAndEmptyArrays) { + $unwind['preserveNullAndEmptyArrays'] = $this->preserveNullAndEmptyArrays; } return ['$unwind' => $unwind]; @@ -51,7 +56,7 @@ public function getExpression(): array * The name of a new field to hold the array index of the element. The name * cannot start with a dollar sign $. */ - public function includeArrayIndex(string $includeArrayIndex): self + public function includeArrayIndex(string $includeArrayIndex): static { $this->includeArrayIndex = $includeArrayIndex; @@ -62,7 +67,7 @@ public function includeArrayIndex(string $includeArrayIndex): self * If true, if the path is null, missing, or an empty array, $unwind outputs * the document. */ - public function preserveNullAndEmptyArrays(bool $preserveNullAndEmptyArrays = true): self + public function preserveNullAndEmptyArrays(bool $preserveNullAndEmptyArrays = true): static { $this->preserveNullAndEmptyArrays = $preserveNullAndEmptyArrays; diff --git a/lib/Doctrine/ODM/MongoDB/DocumentManager.php b/lib/Doctrine/ODM/MongoDB/DocumentManager.php index 8c07a1b603..ac6b7ee24d 100644 --- a/lib/Doctrine/ODM/MongoDB/DocumentManager.php +++ b/lib/Doctrine/ODM/MongoDB/DocumentManager.php @@ -33,7 +33,6 @@ use function array_search; use function assert; -use function get_class; use function gettype; use function is_object; use function ltrim; @@ -745,7 +744,7 @@ public function getConfiguration(): Configuration */ public function createReference(object $document, array $referenceMapping) { - $class = $this->getClassMetadata(get_class($document)); + $class = $this->getClassMetadata($document::class); $id = $this->unitOfWork->getDocumentIdentifier($document); if ($id === null) { @@ -879,7 +878,7 @@ private static function getVersion(): string if (self::$version === null) { try { self::$version = PrettyVersions::getVersion('doctrine/mongodb-odm')->getPrettyVersion(); - } catch (Throwable $t) { + } catch (Throwable) { return 'unknown'; } } diff --git a/lib/Doctrine/ODM/MongoDB/Event/DocumentNotFoundEventArgs.php b/lib/Doctrine/ODM/MongoDB/Event/DocumentNotFoundEventArgs.php index addf2483e5..4a25347064 100644 --- a/lib/Doctrine/ODM/MongoDB/Event/DocumentNotFoundEventArgs.php +++ b/lib/Doctrine/ODM/MongoDB/Event/DocumentNotFoundEventArgs.php @@ -11,17 +11,12 @@ */ final class DocumentNotFoundEventArgs extends LifecycleEventArgs { - /** @var mixed */ - private $identifier; - private bool $disableException = false; /** @param mixed $identifier */ - public function __construct(object $document, DocumentManager $dm, $identifier) + public function __construct(object $document, DocumentManager $dm, private $identifier) { parent::__construct($document, $dm); - - $this->identifier = $identifier; } /** diff --git a/lib/Doctrine/ODM/MongoDB/Event/OnClassMetadataNotFoundEventArgs.php b/lib/Doctrine/ODM/MongoDB/Event/OnClassMetadataNotFoundEventArgs.php index 379a957d81..8119097143 100644 --- a/lib/Doctrine/ODM/MongoDB/Event/OnClassMetadataNotFoundEventArgs.php +++ b/lib/Doctrine/ODM/MongoDB/Event/OnClassMetadataNotFoundEventArgs.php @@ -15,17 +15,12 @@ */ final class OnClassMetadataNotFoundEventArgs extends ManagerEventArgs { - /** @psalm-var class-string */ - private string $className; - /** @var ClassMetadata|null */ private ?ClassMetadata $foundMetadata = null; /** @psalm-param class-string $className */ - public function __construct(string $className, DocumentManager $dm) + public function __construct(private string $className, DocumentManager $dm) { - $this->className = $className; - parent::__construct($dm); } diff --git a/lib/Doctrine/ODM/MongoDB/Event/PostCollectionLoadEventArgs.php b/lib/Doctrine/ODM/MongoDB/Event/PostCollectionLoadEventArgs.php index 27bae3b377..8e0f74ee71 100644 --- a/lib/Doctrine/ODM/MongoDB/Event/PostCollectionLoadEventArgs.php +++ b/lib/Doctrine/ODM/MongoDB/Event/PostCollectionLoadEventArgs.php @@ -15,15 +15,10 @@ */ final class PostCollectionLoadEventArgs extends ManagerEventArgs { - /** @var PersistentCollectionInterface */ - private PersistentCollectionInterface $collection; - /** @param PersistentCollectionInterface $collection */ - public function __construct(PersistentCollectionInterface $collection, DocumentManager $dm) + public function __construct(private PersistentCollectionInterface $collection, DocumentManager $dm) { parent::__construct($dm); - - $this->collection = $collection; } /** diff --git a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php index c8a8af2a48..861d0f8e5c 100644 --- a/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php @@ -21,7 +21,6 @@ use function dirname; use function file_exists; use function file_put_contents; -use function get_class; use function is_dir; use function is_writable; use function mkdir; @@ -448,7 +447,7 @@ public function hydrate(object \$document, array \$data, array \$hints = array() */ public function hydrate(object $document, array $data, array $hints = []): array { - $metadata = $this->dm->getClassMetadata(get_class($document)); + $metadata = $this->dm->getClassMetadata($document::class); // Invoke preLoad lifecycle events and listeners if (! empty($metadata->lifecycleCallbacks[Events::preLoad])) { $args = [new PreLoadEventArgs($document, $this->dm, $data)]; @@ -477,7 +476,7 @@ public function hydrate(object $document, array $data, array $hints = []): array string $method, // we don't care array $parameters, // we don't care &$initializer, - array $properties // we currently do not use this + array $properties, // we currently do not use this ): bool { $initializer = null; diff --git a/lib/Doctrine/ODM/MongoDB/Id/IncrementGenerator.php b/lib/Doctrine/ODM/MongoDB/Id/IncrementGenerator.php index e99535b517..3b0f020690 100644 --- a/lib/Doctrine/ODM/MongoDB/Id/IncrementGenerator.php +++ b/lib/Doctrine/ODM/MongoDB/Id/IncrementGenerator.php @@ -7,8 +7,6 @@ use Doctrine\ODM\MongoDB\DocumentManager; use MongoDB\Operation\FindOneAndUpdate; -use function get_class; - /** * IncrementGenerator is responsible for generating auto increment identifiers. It uses * a collection and generates the next id by using $inc on a field named "current_id". @@ -53,7 +51,7 @@ public function setStartingId(int $startingId): void public function generate(DocumentManager $dm, object $document) { - $className = get_class($document); + $className = $document::class; $db = $dm->getDocumentDatabase($className); $key = $this->key ?: $dm->getDocumentCollection($className)->getCollectionName(); diff --git a/lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php b/lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php index b174909c39..3c4cd318f2 100644 --- a/lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php +++ b/lib/Doctrine/ODM/MongoDB/Iterator/HydratingIterator.php @@ -27,28 +27,14 @@ final class HydratingIterator implements Iterator /** @var Generator>|null */ private $iterator; - private UnitOfWork $unitOfWork; - - /** @var ClassMetadata */ - private ClassMetadata $class; - - /** - * @var array - * @psalm-var Hints - */ - private array $unitOfWorkHints; - /** * @param Traversable> $traversable * @param ClassMetadata $class * @psalm-param Hints $unitOfWorkHints */ - public function __construct(Traversable $traversable, UnitOfWork $unitOfWork, ClassMetadata $class, array $unitOfWorkHints = []) + public function __construct(Traversable $traversable, private UnitOfWork $unitOfWork, private ClassMetadata $class, private array $unitOfWorkHints = []) { - $this->iterator = $this->wrapTraversable($traversable); - $this->unitOfWork = $unitOfWork; - $this->class = $class; - $this->unitOfWorkHints = $unitOfWorkHints; + $this->iterator = $this->wrapTraversable($traversable); } public function __destruct() diff --git a/lib/Doctrine/ODM/MongoDB/Iterator/PrimingIterator.php b/lib/Doctrine/ODM/MongoDB/Iterator/PrimingIterator.php index 929e8093d9..43c21f2787 100644 --- a/lib/Doctrine/ODM/MongoDB/Iterator/PrimingIterator.php +++ b/lib/Doctrine/ODM/MongoDB/Iterator/PrimingIterator.php @@ -20,23 +20,6 @@ */ final class PrimingIterator implements Iterator { - /** @var \Iterator */ - private \Iterator $iterator; - - /** @var ClassMetadata */ - private ClassMetadata $class; - - private ReferencePrimer $referencePrimer; - - /** @var array */ - private array $primers; - - /** - * @var array - * @psalm-var Hints - */ - private array $unitOfWorkHints; - private bool $referencesPrimed = false; /** @@ -45,13 +28,8 @@ final class PrimingIterator implements Iterator * @param array $primers * @psalm-param Hints $unitOfWorkHints */ - public function __construct(\Iterator $iterator, ClassMetadata $class, ReferencePrimer $referencePrimer, array $primers, array $unitOfWorkHints = []) + public function __construct(private \Iterator $iterator, private ClassMetadata $class, private ReferencePrimer $referencePrimer, private array $primers, private array $unitOfWorkHints = []) { - $this->iterator = $iterator; - $this->class = $class; - $this->referencePrimer = $referencePrimer; - $this->primers = $primers; - $this->unitOfWorkHints = $unitOfWorkHints; } public function toArray(): array diff --git a/lib/Doctrine/ODM/MongoDB/LockException.php b/lib/Doctrine/ODM/MongoDB/LockException.php index 1bea959865..b0bd71e697 100644 --- a/lib/Doctrine/ODM/MongoDB/LockException.php +++ b/lib/Doctrine/ODM/MongoDB/LockException.php @@ -6,13 +6,9 @@ final class LockException extends MongoDBException { - private ?object $document; - - public function __construct(string $msg, ?object $document = null) + public function __construct(string $msg, private ?object $document = null) { parent::__construct($msg); - - $this->document = $document; } /** diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractField.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractField.php index 3c4f885825..77e9870e17 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractField.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractField.php @@ -31,7 +31,7 @@ public function __construct( bool $nullable = false, array $options = [], ?string $strategy = null, - bool $notSaved = false + bool $notSaved = false, ) { $this->name = $name; $this->type = $type; diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractIndex.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractIndex.php index 7363a022eb..8d3db18668 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractIndex.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AbstractIndex.php @@ -18,9 +18,6 @@ abstract class AbstractIndex implements Annotation /** @var int|null */ public $expireAfterSeconds; - /** @var string|int|null */ - public $order; - /** @var bool */ public $unique; @@ -44,17 +41,16 @@ public function __construct( ?string $name = null, ?bool $background = null, ?int $expireAfterSeconds = null, - $order = null, + public $order = null, bool $unique = false, bool $sparse = false, array $options = [], - array $partialFilterExpression = [] + array $partialFilterExpression = [], ) { $this->keys = $keys; $this->name = $name; $this->background = $background; $this->expireAfterSeconds = $expireAfterSeconds; - $this->order = $order; $this->unique = $unique; $this->sparse = $sparse; $this->options = $options; diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AlsoLoad.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AlsoLoad.php index 66efc2f468..e847775cf7 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AlsoLoad.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/AlsoLoad.php @@ -16,16 +16,8 @@ #[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_METHOD)] final class AlsoLoad implements Annotation { - /** @var string|string[] */ - public $value; - - /** @var string|null */ - public $name; - /** @param string|string[] $value */ - public function __construct($value, ?string $name = null) + public function __construct(public $value, public ?string $name = null) { - $this->value = $value; - $this->name = $name; } } diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php index a437a2be6e..c9754ce291 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Document.php @@ -19,9 +19,6 @@ final class Document extends AbstractDocument /** @var string|null */ public $db; - /** @var string|array{name: string, capped?: bool, size?: int, max?: int}|null */ - public $collection; - /** @var string|null */ public $repositoryClass; @@ -34,9 +31,6 @@ final class Document extends AbstractDocument /** @var string|null */ public $shardKey; - /** @var string|int|null */ - public $writeConcern; - /** * @param string|array{name: string, capped?: bool, size?: int, max?: int}|null $collection * @param Index[] $indexes @@ -44,19 +38,17 @@ final class Document extends AbstractDocument */ public function __construct( ?string $db = null, - $collection = null, + public $collection = null, ?string $repositoryClass = null, array $indexes = [], bool $readOnly = false, ?string $shardKey = null, - $writeConcern = null + public $writeConcern = null, ) { $this->db = $db; - $this->collection = $collection; $this->repositoryClass = $repositoryClass; $this->indexes = $indexes; $this->readOnly = $readOnly; $this->shardKey = $shardKey; - $this->writeConcern = $writeConcern; } } diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedMany.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedMany.php index e4c5d68e58..a8f3adb22e 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedMany.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedMany.php @@ -47,7 +47,7 @@ public function __construct( ?string $discriminatorField = null, ?array $discriminatorMap = null, ?string $defaultDiscriminatorValue = null, - ?string $collectionClass = null + ?string $collectionClass = null, ) { parent::__construct($name, ClassMetadata::MANY, $nullable, $options, $strategy, $notSaved); diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedOne.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedOne.php index c4088e9d75..6a260ec0e2 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedOne.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/EmbedOne.php @@ -42,7 +42,7 @@ public function __construct( ?string $targetDocument = null, ?string $discriminatorField = null, ?array $discriminatorMap = null, - ?string $defaultDiscriminatorValue = null + ?string $defaultDiscriminatorValue = null, ) { parent::__construct($name, ClassMetadata::ONE, $nullable, $options, $strategy, $notSaved); diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Field.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Field.php index 7f82c01135..39c22362b7 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Field.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Field.php @@ -31,7 +31,7 @@ public function __construct( array $options = [], ?string $strategy = null, bool $notSaved = false, - ?string $enumType = null + ?string $enumType = null, ) { parent::__construct($name, $type, $nullable, $options, $strategy, $notSaved); diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File.php index bffe0d097b..2421cfe53a 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File.php @@ -34,9 +34,6 @@ final class File extends AbstractDocument /** @var string|null */ public $shardKey; - /** @var string|int|null */ - public $writeConcern; - /** @var int|null */ public $chunkSizeBytes; @@ -51,8 +48,8 @@ public function __construct( array $indexes = [], bool $readOnly = false, ?string $shardKey = null, - $writeConcern = null, - ?int $chunkSizeBytes = null + public $writeConcern = null, + ?int $chunkSizeBytes = null, ) { $this->db = $db; $this->bucketName = $bucketName; @@ -60,7 +57,6 @@ public function __construct( $this->indexes = $indexes; $this->readOnly = $readOnly; $this->shardKey = $shardKey; - $this->writeConcern = $writeConcern; $this->chunkSizeBytes = $chunkSizeBytes; } } diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File/Metadata.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File/Metadata.php index aec10be4fb..16c06265ce 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File/Metadata.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/File/Metadata.php @@ -40,7 +40,7 @@ public function __construct( ?string $targetDocument = null, ?string $discriminatorField = null, ?array $discriminatorMap = null, - ?string $defaultDiscriminatorValue = null + ?string $defaultDiscriminatorValue = null, ) { parent::__construct('metadata', ClassMetadata::ONE, $nullable, $options, $strategy, $notSaved); diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Id.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Id.php index a8b6ec1cfb..ab48d9d8b2 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Id.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/Id.php @@ -25,7 +25,7 @@ public function __construct( bool $nullable = false, array $options = [], ?string $strategy = 'auto', - bool $notSaved = false + bool $notSaved = false, ) { parent::__construct($name, $type, $nullable, $options, $strategy, $notSaved); } diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceMany.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceMany.php index 256cc6b7c6..235d9be92b 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceMany.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceMany.php @@ -36,9 +36,6 @@ final class ReferenceMany extends AbstractField /** @var string|null */ public $defaultDiscriminatorValue; - /** @var string[]|string|null */ - public $cascade; - /** @var bool|null */ public $orphanRemoval; @@ -87,7 +84,7 @@ public function __construct( ?string $discriminatorField = null, ?array $discriminatorMap = null, ?string $defaultDiscriminatorValue = null, - $cascade = null, + public $cascade = null, ?bool $orphanRemoval = null, ?string $inversedBy = null, ?string $mappedBy = null, @@ -97,7 +94,7 @@ public function __construct( ?int $limit = null, ?int $skip = null, ?string $collectionClass = null, - array $prime = [] + array $prime = [], ) { parent::__construct($name, ClassMetadata::MANY, $nullable, $options, $strategy, $notSaved); @@ -106,7 +103,6 @@ public function __construct( $this->discriminatorField = $discriminatorField; $this->discriminatorMap = $discriminatorMap; $this->defaultDiscriminatorValue = $defaultDiscriminatorValue; - $this->cascade = $cascade; $this->orphanRemoval = $orphanRemoval; $this->inversedBy = $inversedBy; $this->mappedBy = $mappedBy; diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceOne.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceOne.php index 6021d4dd3b..0602805d7c 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceOne.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/ReferenceOne.php @@ -35,9 +35,6 @@ final class ReferenceOne extends AbstractField /** @var string|null */ public $defaultDiscriminatorValue; - /** @var string[]|string|null */ - public $cascade; - /** @var bool|null */ public $orphanRemoval; @@ -80,7 +77,7 @@ public function __construct( ?string $discriminatorField = null, ?array $discriminatorMap = null, ?string $defaultDiscriminatorValue = null, - $cascade = null, + public $cascade = null, ?bool $orphanRemoval = null, ?string $inversedBy = null, ?string $mappedBy = null, @@ -88,7 +85,7 @@ public function __construct( array $sort = [], array $criteria = [], ?int $limit = null, - ?int $skip = null + ?int $skip = null, ) { parent::__construct($name, ClassMetadata::ONE, $nullable, $options, $strategy, $notSaved); @@ -97,7 +94,6 @@ public function __construct( $this->discriminatorField = $discriminatorField; $this->discriminatorMap = $discriminatorMap; $this->defaultDiscriminatorValue = $defaultDiscriminatorValue; - $this->cascade = $cascade; $this->orphanRemoval = $orphanRemoval; $this->inversedBy = $inversedBy; $this->mappedBy = $mappedBy; diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/UniqueIndex.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/UniqueIndex.php index b43d11ca41..371fe66e25 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/UniqueIndex.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/UniqueIndex.php @@ -24,7 +24,7 @@ public function __construct( $order = null, bool $sparse = false, array $options = [], - array $partialFilterExpression = [] + array $partialFilterExpression = [], ) { parent::__construct( $keys, diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/View.php b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/View.php index 70226cfd70..adab26dc92 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/View.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Annotations/View.php @@ -30,7 +30,7 @@ public function __construct( ?string $db = null, ?string $view = null, ?string $rootClass = null, - ?string $repositoryClass = null + ?string $repositoryClass = null, ) { $this->db = $db; $this->view = $view; diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php index 23956561e7..e4cd02fbd4 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadata.php @@ -40,7 +40,6 @@ use function count; use function enum_exists; use function extension_loaded; -use function get_class; use function in_array; use function is_array; use function is_string; @@ -902,7 +901,7 @@ public function invokeLifecycleCallbacks(string $event, object $document, ?array } if (! $document instanceof $this->name) { - throw new InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, get_class($document))); + throw new InvalidArgumentException(sprintf('Expected document class "%s"; found: "%s"', $this->name, $document::class)); } if (empty($this->lifecycleCallbacks[$event])) { diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php index 6f20f8d3bf..e986c87d8b 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/ClassMetadataFactory.php @@ -23,7 +23,6 @@ use ReflectionException; use function assert; -use function get_class; use function get_class_methods; use function in_array; use function interface_exists; @@ -286,14 +285,14 @@ private function completeIdGeneratorMapping(ClassMetadata $class): void $customGenerator = new $idGenOptions['class'](); unset($idGenOptions['class']); if (! $customGenerator instanceof IdGenerator) { - throw MappingException::classIsNotAValidGenerator(get_class($customGenerator)); + throw MappingException::classIsNotAValidGenerator($customGenerator::class); } $methods = get_class_methods($customGenerator); foreach ($idGenOptions as $name => $value) { $method = 'set' . ucfirst($name); if (! in_array($method, $methods)) { - throw MappingException::missingGeneratorSetter(get_class($customGenerator), $name); + throw MappingException::missingGeneratorSetter($customGenerator::class, $name); } $customGenerator->$method($value); diff --git a/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php b/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php index c7828194ba..07517f9a78 100644 --- a/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php +++ b/lib/Doctrine/ODM/MongoDB/Mapping/Driver/AnnotationDriver.php @@ -23,7 +23,6 @@ use function class_exists; use function constant; use function count; -use function get_class; use function interface_exists; use function is_array; use function MongoDB\BSON\fromJSON; @@ -82,7 +81,7 @@ public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\C $documentAnnot = null; foreach ($classAnnotations as $annot) { - $classAnnotations[get_class($annot)] = $annot; + $classAnnotations[$annot::class] = $annot; if ($annot instanceof ODM\AbstractDocument) { if ($documentAnnot !== null) { @@ -205,7 +204,7 @@ public function loadMetadataForClass($className, \Doctrine\Persistence\Mapping\C '2.2', 'The "indexes" parameter in the "%s" annotation for class "%s" is deprecated. Specify all "@Index" and "@UniqueIndex" annotations on the class.', $className, - get_class($documentAnnot), + $documentAnnot::class, ); foreach ($documentAnnot->indexes as $index) { diff --git a/lib/Doctrine/ODM/MongoDB/MongoDBException.php b/lib/Doctrine/ODM/MongoDB/MongoDBException.php index 9b95abb1e1..5d6582ac73 100644 --- a/lib/Doctrine/ODM/MongoDB/MongoDBException.php +++ b/lib/Doctrine/ODM/MongoDB/MongoDBException.php @@ -10,7 +10,6 @@ use function array_slice; use function end; -use function get_class; use function implode; use function is_array; use function is_object; @@ -78,7 +77,7 @@ public static function invalidValueForType(string $type, $expected, $got): self } if (is_object($got)) { - $gotType = get_class($got); + $gotType = $got::class; } elseif (is_array($got)) { $gotType = 'array'; } else { diff --git a/lib/Doctrine/ODM/MongoDB/PersistentCollection/DefaultPersistentCollectionGenerator.php b/lib/Doctrine/ODM/MongoDB/PersistentCollection/DefaultPersistentCollectionGenerator.php index 3bc9c221cf..6091c640ee 100644 --- a/lib/Doctrine/ODM/MongoDB/PersistentCollection/DefaultPersistentCollectionGenerator.php +++ b/lib/Doctrine/ODM/MongoDB/PersistentCollection/DefaultPersistentCollectionGenerator.php @@ -292,7 +292,7 @@ private function getMethodReturnType(ReflectionMethod $method): string private function formatType( ReflectionType $type, ReflectionMethod $method, - ?ReflectionParameter $parameter = null + ?ReflectionParameter $parameter = null, ): string { if ($type instanceof ReflectionUnionType) { return implode('|', array_map( diff --git a/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionException.php b/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionException.php index 543c3fe2f8..abb0d49789 100644 --- a/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionException.php +++ b/lib/Doctrine/ODM/MongoDB/PersistentCollection/PersistentCollectionException.php @@ -33,7 +33,7 @@ public static function invalidParameterTypeHint( string $className, string $methodName, string $parameterName, - ?Throwable $previous = null + ?Throwable $previous = null, ): self { return new self( sprintf( diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php b/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php index dc0597ff52..64ab6a641a 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php @@ -25,7 +25,6 @@ use function assert; use function count; use function end; -use function get_class; use function implode; use function sort; use function strpos; @@ -44,17 +43,8 @@ */ final class CollectionPersister { - private DocumentManager $dm; - - private PersistenceBuilder $pb; - - private UnitOfWork $uow; - - public function __construct(DocumentManager $dm, PersistenceBuilder $pb, UnitOfWork $uow) + public function __construct(private DocumentManager $dm, private PersistenceBuilder $pb, private UnitOfWork $uow) { - $this->dm = $dm; - $this->pb = $pb; - $this->uow = $uow; } /** @@ -444,7 +434,7 @@ private function getPathAndParent(PersistentCollectionInterface $coll): array */ private function executeQuery(object $document, array $newObj, array $options): void { - $className = get_class($document); + $className = $document::class; $class = $this->dm->getClassMetadata($className); $id = $class->getDatabaseIdentifierValue($this->uow->getDocumentIdentifier($document)); $query = ['_id' => $id]; diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php index 69ea29eaef..734995e220 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php @@ -52,7 +52,6 @@ use function assert; use function count; use function explode; -use function get_class; use function get_object_vars; use function gettype; use function implode; @@ -82,15 +81,6 @@ */ final class DocumentPersister { - private PersistenceBuilder $pb; - - private DocumentManager $dm; - - private UnitOfWork $uow; - - /** @psalm-var ClassMetadata */ - private ClassMetadata $class; - private ?Collection $collection = null; private ?Bucket $bucket = null; @@ -113,24 +103,17 @@ final class DocumentPersister private CollectionPersister $cp; - private HydratorFactory $hydratorFactory; - /** @psalm-param ClassMetadata $class */ public function __construct( - PersistenceBuilder $pb, - DocumentManager $dm, - UnitOfWork $uow, - HydratorFactory $hydratorFactory, - ClassMetadata $class, - ?CriteriaMerger $cm = null + private PersistenceBuilder $pb, + private DocumentManager $dm, + private UnitOfWork $uow, + private HydratorFactory $hydratorFactory, + private ClassMetadata $class, + ?CriteriaMerger $cm = null, ) { - $this->pb = $pb; - $this->dm = $dm; - $this->cm = $cm ?: new CriteriaMerger(); - $this->uow = $uow; - $this->hydratorFactory = $hydratorFactory; - $this->class = $class; - $this->cp = $this->uow->getCollectionPersister(); + $this->cm = $cm ?: new CriteriaMerger(); + $this->cp = $this->uow->getCollectionPersister(); if ($class->isEmbeddedDocument || $class->isQueryResultDocument) { return; @@ -710,7 +693,7 @@ private function loadEmbedManyCollection(PersistentCollectionInterface $collecti $embeddedDocumentObject = $embeddedMetadata->newInstance(); if (! is_array($embeddedDocument)) { - throw HydratorException::associationItemTypeMismatch(get_class($owner), $mapping['name'], $key, 'array', gettype($embeddedDocument)); + throw HydratorException::associationItemTypeMismatch($owner::class, $mapping['name'], $key, 'array', gettype($embeddedDocument)); } $this->uow->setParentAssociation($embeddedDocumentObject, $mapping, $owner, $mapping['name'] . '.' . $key); @@ -747,7 +730,7 @@ private function loadReferenceManyCollectionOwningSide(PersistentCollectionInter $className = $this->uow->getClassNameForAssociation($mapping, $reference); if ($mapping['storeAs'] !== ClassMetadata::REFERENCE_STORE_AS_ID && ! is_array($reference)) { - throw HydratorException::associationItemTypeMismatch(get_class($owner), $mapping['name'], $key, 'array', gettype($reference)); + throw HydratorException::associationItemTypeMismatch($owner::class, $mapping['name'], $key, 'array', gettype($reference)); } $identifier = ClassMetadata::getReferenceId($reference, $mapping['storeAs']); @@ -839,7 +822,7 @@ public function createReferenceManyInverseSideQuery(PersistentCollectionInterfac throw PersistentCollectionException::ownerRequiredToLoadCollection(); } - $ownerClass = $this->dm->getClassMetadata(get_class($owner)); + $ownerClass = $this->dm->getClassMetadata($owner::class); $targetClass = $this->dm->getClassMetadata($mapping['targetDocument']); $mappedByMapping = $targetClass->fieldMappings[$mapping['mappedBy']] ?? []; $mappedByFieldName = ClassMetadata::getReferenceFieldName($mappedByMapping['storeAs'] ?? ClassMetadata::REFERENCE_STORE_AS_DB_REF, $mapping['mappedBy']); @@ -1175,7 +1158,7 @@ private function prepareQueryElement(string $fieldName, $value = null, ?ClassMet // Prepare mapped, embedded objects if ( ! empty($mapping['embedded']) && is_object($value) && - ! $this->dm->getMetadataFactory()->isTransient(get_class($value)) + ! $this->dm->getMetadataFactory()->isTransient($value::class) ) { return [[$fieldName, $this->pb->prepareEmbeddedDocumentValue($mapping, $value)]]; } @@ -1183,7 +1166,7 @@ private function prepareQueryElement(string $fieldName, $value = null, ?ClassMet if (! empty($mapping['reference']) && is_object($value) && ! ($value instanceof ObjectId)) { try { return $this->prepareReference($fieldName, $value, $mapping, $inNewObj); - } catch (MappingException $e) { + } catch (MappingException) { // do nothing in case passed object is not mapped document } } diff --git a/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php b/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php index c7331f003b..862e79ba5d 100644 --- a/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php +++ b/lib/Doctrine/ODM/MongoDB/Persisters/PersistenceBuilder.php @@ -18,7 +18,6 @@ use function array_search; use function array_values; use function assert; -use function get_class; /** * PersistenceBuilder builds the queries used by the persisters to update and insert @@ -59,7 +58,7 @@ public function __construct(DocumentManager $dm, UnitOfWork $uow) */ public function prepareInsertData($document) { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $changeset = $this->uow->getDocumentChangeSet($document); $insertData = []; @@ -124,7 +123,7 @@ public function prepareInsertData($document) */ public function prepareUpdateData($document) { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $changeset = $this->uow->getDocumentChangeSet($document); $updateData = []; @@ -234,7 +233,7 @@ public function prepareUpdateData($document) */ public function prepareUpsertData($document) { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $changeset = $this->uow->getDocumentChangeSet($document); $updateData = []; @@ -356,7 +355,7 @@ public function prepareReferencedDocumentValue(array $referenceMapping, $documen public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDocument, $includeNestedCollections = false) { $embeddedDocumentValue = []; - $class = $this->dm->getClassMetadata(get_class($embeddedDocument)); + $class = $this->dm->getClassMetadata($embeddedDocument::class); foreach ($class->fieldMappings as $mapping) { // Skip notSaved fields diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php index 9d5e56938b..e7e533dfca 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php @@ -92,17 +92,17 @@ public function generateProxyClasses(array $classes): int */ private function createInitializer( ClassMetadata $metadata, - DocumentPersister $documentPersister + DocumentPersister $documentPersister, ): Closure { return function ( GhostObjectInterface $ghostObject, string $method, // we don't care array $parameters, // we don't care &$initializer, - array $properties // we currently do not use this + array $properties, // we currently do not use this ) use ( $metadata, - $documentPersister + $documentPersister, ): bool { $originalInitializer = $initializer; $initializer = null; diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/CachingClassNameResolver.php b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/CachingClassNameResolver.php index ff25ba2b16..8c2879ed34 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/CachingClassNameResolver.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/CachingClassNameResolver.php @@ -9,14 +9,11 @@ /** @internal */ final class CachingClassNameResolver implements ClassNameResolver, ProxyClassNameResolver { - private ProxyClassNameResolver $resolver; - /** @var array */ private array $resolvedNames = []; - public function __construct(ProxyClassNameResolver $resolver) + public function __construct(private ProxyClassNameResolver $resolver) { - $this->resolver = $resolver; } /** diff --git a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ProxyManagerClassNameResolver.php b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ProxyManagerClassNameResolver.php index 78151a3133..c281cfc120 100644 --- a/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ProxyManagerClassNameResolver.php +++ b/lib/Doctrine/ODM/MongoDB/Proxy/Resolver/ProxyManagerClassNameResolver.php @@ -12,11 +12,8 @@ /** @internal */ final class ProxyManagerClassNameResolver implements ClassNameResolver, ProxyClassNameResolver { - private Configuration $configuration; - - public function __construct(Configuration $configuration) + public function __construct(private Configuration $configuration) { - $this->configuration = $configuration; } public function getRealClass(string $class): string diff --git a/lib/Doctrine/ODM/MongoDB/Query/Expr.php b/lib/Doctrine/ODM/MongoDB/Query/Expr.php index eb238ed2ad..414094fa42 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/Expr.php +++ b/lib/Doctrine/ODM/MongoDB/Query/Expr.php @@ -162,7 +162,7 @@ public function addOr($expression, ...$expressions): self */ public function addToSet($valueOrExpression): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$addToSet'][$this->currentField] = static::convertExpression($valueOrExpression, $this->class); return $this; @@ -188,7 +188,7 @@ public function all(array $values): self */ protected function bit(string $operator, int $value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$bit'][$this->currentField][$operator] = $value; return $this; @@ -227,7 +227,7 @@ public function bitOr(int $value): self */ public function bitsAllClear($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); return $this->operator('$bitsAllClear', $value); } @@ -243,7 +243,7 @@ public function bitsAllClear($value): self */ public function bitsAllSet($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); return $this->operator('$bitsAllSet', $value); } @@ -259,7 +259,7 @@ public function bitsAllSet($value): self */ public function bitsAnyClear($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); return $this->operator('$bitsAnyClear', $value); } @@ -275,7 +275,7 @@ public function bitsAnyClear($value): self */ public function bitsAnySet($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); return $this->operator('$bitsAnySet', $value); } @@ -345,7 +345,7 @@ public function currentDate(string $type = 'date'): self throw new InvalidArgumentException('Type for currentDate operator must be date or timestamp.'); } - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$currentDate'][$this->currentField]['$type'] = $type; return $this; @@ -646,7 +646,7 @@ public function in(array $values): self */ public function inc($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$inc'][$this->currentField] = $value; return $this; @@ -657,7 +657,7 @@ public function inc($value): self */ public function includesReferenceTo(object $document): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $mapping = $this->getReferenceMapping(); $reference = $this->dm->createReference($document, $mapping); $storeAs = $mapping['storeAs'] ?? null; @@ -755,7 +755,7 @@ public function lte($value): self */ public function max($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$max'][$this->currentField] = $value; return $this; @@ -771,7 +771,7 @@ public function max($value): self */ public function min($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$min'][$this->currentField] = $value; return $this; @@ -803,7 +803,7 @@ public function mod($divisor, $remainder = 0): self */ public function mul($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$mul'][$this->currentField] = $value; return $this; @@ -929,7 +929,7 @@ public function operator(string $operator, $value): self */ public function popFirst(): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$pop'][$this->currentField] = -1; return $this; @@ -943,7 +943,7 @@ public function popFirst(): self */ public function popLast(): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$pop'][$this->currentField] = 1; return $this; @@ -973,7 +973,7 @@ public function position(int $position): self */ public function pull($valueOrExpression): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$pull'][$this->currentField] = static::convertExpression($valueOrExpression, $this->class); return $this; @@ -990,7 +990,7 @@ public function pull($valueOrExpression): self */ public function pullAll(array $values): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$pullAll'][$this->currentField] = $values; return $this; @@ -1024,7 +1024,7 @@ public function push($valueOrExpression): self ); } - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$push'][$this->currentField] = $valueOrExpression; return $this; @@ -1051,7 +1051,7 @@ public function range($start, $end): self */ public function references(object $document): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $mapping = $this->getReferenceMapping(); $reference = $this->dm->createReference($document, $mapping); $storeAs = $mapping['storeAs'] ?? null; @@ -1100,7 +1100,7 @@ public function references(object $document): self */ public function rename(string $name): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$rename'][$this->currentField] = $name; return $this; @@ -1120,7 +1120,7 @@ public function rename(string $name): self */ public function set($value, bool $atomic = true): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); assert($this->currentField !== null); if ($atomic) { @@ -1184,7 +1184,7 @@ public function setNewObj(array $newObj): self */ public function setOnInsert($value): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$setOnInsert'][$this->currentField] = $value; return $this; @@ -1289,7 +1289,7 @@ public function type($type): self */ public function unsetField(): self { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); $this->newObj['$unset'][$this->currentField] = 1; return $this; @@ -1319,7 +1319,7 @@ public function where($javascript): self */ private function getReferenceMapping(): array { - $this->requiresCurrentField(); + $this->requiresCurrentField(__METHOD__); assert($this->currentField !== null); try { @@ -1368,10 +1368,10 @@ private function normalizeSortOrder($order): int * * @throws LogicException If a current field has not been set. */ - private function requiresCurrentField(): void + private function requiresCurrentField(string $method): void { if (! $this->currentField) { - throw new LogicException('This method requires you set a current field using field().'); + throw new LogicException(sprintf('%s requires setting a current field using field().', $method)); } } diff --git a/lib/Doctrine/ODM/MongoDB/Query/QueryExpressionVisitor.php b/lib/Doctrine/ODM/MongoDB/Query/QueryExpressionVisitor.php index e9fe6f1d7a..599ebb0ba2 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/QueryExpressionVisitor.php +++ b/lib/Doctrine/ODM/MongoDB/Query/QueryExpressionVisitor.php @@ -45,11 +45,8 @@ final class QueryExpressionVisitor extends ExpressionVisitor CompositeExpression::TYPE_OR => 'addOr', ]; - protected Builder $builder; - - public function __construct(Builder $builder) + public function __construct(protected Builder $builder) { - $this->builder = $builder; } /** diff --git a/lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php b/lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php index 36412aa3a2..9d6a86dadd 100644 --- a/lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php +++ b/lib/Doctrine/ODM/MongoDB/Query/ReferencePrimer.php @@ -22,7 +22,6 @@ use function call_user_func; use function count; use function explode; -use function get_class; use function implode; use function is_object; use function serialize; @@ -140,7 +139,7 @@ public function primeReferences(ClassMetadata $class, $documents, string $fieldN } if ($mapping['type'] === ClassMetadata::ONE && $fieldValue instanceof GhostObjectInterface && ! $fieldValue->isProxyInitialized()) { - $refClass = $this->dm->getClassMetadata(get_class($fieldValue)); + $refClass = $this->dm->getClassMetadata($fieldValue::class); $id = $this->uow->getDocumentIdentifier($fieldValue); $groupedIds[$refClass->name][serialize($id)] = $id; } elseif ($mapping['type'] === ClassMetadata::MANY && $fieldValue instanceof PersistentCollectionInterface) { diff --git a/lib/Doctrine/ODM/MongoDB/Repository/DefaultGridFSRepository.php b/lib/Doctrine/ODM/MongoDB/Repository/DefaultGridFSRepository.php index 1486f15879..586c56ab16 100644 --- a/lib/Doctrine/ODM/MongoDB/Repository/DefaultGridFSRepository.php +++ b/lib/Doctrine/ODM/MongoDB/Repository/DefaultGridFSRepository.php @@ -28,7 +28,7 @@ public function openDownloadStream($id) { try { return $this->getDocumentBucket()->openDownloadStream($this->class->getDatabaseIdentifierValue($id)); - } catch (FileNotFoundException $e) { + } catch (FileNotFoundException) { throw DocumentNotFoundException::documentNotFound($this->getClassName(), $id); } } @@ -38,7 +38,7 @@ public function downloadToStream($id, $destination): void { try { $this->getDocumentBucket()->downloadToStream($this->class->getDatabaseIdentifierValue($id), $destination); - } catch (FileNotFoundException $e) { + } catch (FileNotFoundException) { throw DocumentNotFoundException::documentNotFound($this->getClassName(), $id); } } diff --git a/lib/Doctrine/ODM/MongoDB/SchemaManager.php b/lib/Doctrine/ODM/MongoDB/SchemaManager.php index aef43bc4bf..39d43d9728 100644 --- a/lib/Doctrine/ODM/MongoDB/SchemaManager.php +++ b/lib/Doctrine/ODM/MongoDB/SchemaManager.php @@ -54,14 +54,8 @@ final class SchemaManager 'name', ]; - protected DocumentManager $dm; - - protected ClassMetadataFactory $metadataFactory; - - public function __construct(DocumentManager $dm, ClassMetadataFactory $cmf) + public function __construct(protected DocumentManager $dm, protected ClassMetadataFactory $metadataFactory) { - $this->dm = $dm; - $this->metadataFactory = $cmf; } /** diff --git a/lib/Doctrine/ODM/MongoDB/Types/DateImmutableType.php b/lib/Doctrine/ODM/MongoDB/Types/DateImmutableType.php index 38fe5467fa..bff1e957eb 100644 --- a/lib/Doctrine/ODM/MongoDB/Types/DateImmutableType.php +++ b/lib/Doctrine/ODM/MongoDB/Types/DateImmutableType.php @@ -9,7 +9,6 @@ use DateTimeInterface; use RuntimeException; -use function get_class; use function sprintf; class DateImmutableType extends DateType @@ -30,7 +29,7 @@ public static function getDateTime($value): DateTimeInterface throw new RuntimeException(sprintf( '%s::getDateTime has returned an unsupported implementation of DateTimeInterface: %s', parent::class, - get_class($datetime), + $datetime::class, )); } diff --git a/lib/Doctrine/ODM/MongoDB/Types/IdType.php b/lib/Doctrine/ODM/MongoDB/Types/IdType.php index 5f35e9dc87..b3583a3cab 100644 --- a/lib/Doctrine/ODM/MongoDB/Types/IdType.php +++ b/lib/Doctrine/ODM/MongoDB/Types/IdType.php @@ -21,7 +21,7 @@ public function convertToDatabaseValue($value) if (! $value instanceof ObjectId) { try { $value = new ObjectId((string) $value); - } catch (InvalidArgumentException $e) { + } catch (InvalidArgumentException) { $value = new ObjectId(); } } diff --git a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php index a50be83217..4337f5fb17 100644 --- a/lib/Doctrine/ODM/MongoDB/UnitOfWork.php +++ b/lib/Doctrine/ODM/MongoDB/UnitOfWork.php @@ -482,7 +482,7 @@ private function getClassesForCommitAction(array $documents, bool $includeEmbedd $divided = []; $embeds = []; foreach ($documents as $oid => $d) { - $className = get_class($d); + $className = $d::class; if (isset($embeds[$className])) { continue; } @@ -520,7 +520,7 @@ private function getClassesForCommitAction(array $documents, bool $includeEmbedd private function computeScheduleInsertsChangeSets(): void { foreach ($this->documentInsertions as $document) { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); if ($class->isEmbeddedDocument || $class->isView()) { continue; } @@ -537,7 +537,7 @@ private function computeScheduleInsertsChangeSets(): void private function computeScheduleUpsertsChangeSets(): void { foreach ($this->documentUpserts as $document) { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); if ($class->isEmbeddedDocument || $class->isView()) { continue; } @@ -580,7 +580,7 @@ public function setDocumentChangeSet(object $document, array $changeset): void */ public function getDocumentActualData(object $document): array { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $actualData = []; foreach ($class->reflFields as $name => $refProp) { $mapping = $class->fieldMappings[$name]; @@ -642,7 +642,7 @@ public function getDocumentActualData(object $document): array public function computeChangeSet(ClassMetadata $class, object $document): void { if (! $class->isInheritanceTypeNone()) { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); } // Fire PreFlush lifecycle callbacks @@ -714,7 +714,7 @@ private function computeOrRecomputeChangeSet(ClassMetadata $class, object $docum try { $gridFSMetadata = $class->getFieldMappingByDbFieldName('metadata'); $gridFSMetadataProperty = $gridFSMetadata['fieldName']; - } catch (MappingException $e) { + } catch (MappingException) { } } @@ -936,7 +936,7 @@ public function computeChangeSets(): void private function computeAssociationChanges(object $parentDocument, array $assoc, $value): void { $isNewParentDocument = isset($this->documentInsertions[spl_object_hash($parentDocument)]); - $class = $this->dm->getClassMetadata(get_class($parentDocument)); + $class = $this->dm->getClassMetadata($parentDocument::class); $topOrExistingDocument = ( ! $isNewParentDocument || ! $class->isEmbeddedDocument); if ($value instanceof GhostObjectInterface && ! $value->isProxyInitialized()) { @@ -967,11 +967,11 @@ private function computeAssociationChanges(object $parentDocument, array $assoc, foreach ($unwrappedValue as $key => $entry) { if (! is_object($entry)) { throw new InvalidArgumentException( - sprintf('Expected object, found "%s" in %s::%s', $entry, get_class($parentDocument), $assoc['name']), + sprintf('Expected object, found "%s" in %s::%s', $entry, $parentDocument::class, $assoc['name']), ); } - $targetClass = $this->dm->getClassMetadata(get_class($entry)); + $targetClass = $this->dm->getClassMetadata($entry::class); $state = $this->getDocumentState($entry, self::STATE_NEW); @@ -1072,7 +1072,7 @@ public function recomputeSingleDocumentChangeSet(ClassMetadata $class, object $d } if (! $class->isInheritanceTypeNone()) { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); } $this->computeOrRecomputeChangeSet($class, $document, true); @@ -1098,14 +1098,14 @@ private function persistNew(ClassMetadata $class, object $document): void if ($class->generatorType === ClassMetadata::GENERATOR_TYPE_NONE && $idValue === null) { throw new InvalidArgumentException(sprintf( '%s uses NONE identifier generation strategy but no identifier was provided when persisting.', - get_class($document), + $document::class, )); } if ($class->generatorType === ClassMetadata::GENERATOR_TYPE_AUTO && $idValue !== null && ! preg_match('#^[0-9a-f]{24}$#', (string) $idValue)) { throw new InvalidArgumentException(sprintf( '%s uses AUTO identifier generation strategy but provided identifier is not a valid ObjectId.', - get_class($document), + $document::class, )); } @@ -1395,7 +1395,7 @@ public function isScheduledForUpdate(object $document): bool */ public function isScheduledForSynchronization(object $document): bool { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); return isset($this->scheduledForSynchronization[$class->name][spl_object_hash($document)]); } @@ -1480,7 +1480,7 @@ public function isDocumentScheduled(object $document): bool */ public function addToIdentityMap(object $document): bool { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $id = $this->getIdForIdentityMap($document); if (isset($this->identityMap[$class->name][$id])) { @@ -1515,7 +1515,7 @@ public function getDocumentState(object $document, ?int $assume = null): int return $this->documentStates[$oid]; } - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); if ($class->isEmbeddedDocument) { return self::STATE_NEW; @@ -1575,7 +1575,7 @@ public function removeFromIdentityMap(object $document): bool return false; } - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $id = $this->getIdForIdentityMap($document); if (isset($this->identityMap[$class->name][$id])) { @@ -1651,7 +1651,7 @@ public function tryGetById($id, ClassMetadata $class) */ public function scheduleForSynchronization(object $document): void { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $this->scheduledForSynchronization[$class->name][spl_object_hash($document)] = $document; } @@ -1668,7 +1668,7 @@ public function isInIdentityMap(object $document): bool return false; } - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $id = $this->getIdForIdentityMap($document); return isset($this->identityMap[$class->name][$id]); @@ -1676,7 +1676,7 @@ public function isInIdentityMap(object $document): bool private function getIdForIdentityMap(object $document): string { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); if (! $class->identifier) { $id = spl_object_hash($document); @@ -1710,7 +1710,7 @@ public function containsId($id, string $rootClassName): bool */ public function persist(object $document): void { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); if ($class->isMappedSuperclass || $class->isQueryResultDocument) { throw MongoDBException::cannotPersistMappedSuperclass($class->name); } @@ -1741,7 +1741,7 @@ private function doPersist(object $document, array &$visited): void $visited[$oid] = $document; // Mark visited - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $documentState = $this->getDocumentState($document, self::STATE_NEW); switch ($documentState) { @@ -1819,7 +1819,7 @@ private function doRemove(object $document, array &$visited): void */ $this->cascadeRemove($document, $visited); - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $documentState = $this->getDocumentState($document); switch ($documentState) { case self::STATE_NEW: @@ -1871,7 +1871,7 @@ private function doMerge(object $document, array &$visited, ?object $prevManaged $visited[$oid] = $document; // mark visited - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); /* First we assume DETACHED, although it can still be NEW but we can * avoid an extra DB round trip this way. If it is not MANAGED but has @@ -1951,7 +1951,7 @@ private function doMerge(object $document, array &$visited, ?object $prevManaged continue; } elseif (! $assoc2['isCascadeMerge']) { if ($this->getDocumentState($other) === self::STATE_DETACHED) { - $targetDocument = $assoc2['targetDocument'] ?? get_class($other); + $targetDocument = $assoc2['targetDocument'] ?? $other::class; $targetClass = $this->dm->getClassMetadata($targetDocument); $relatedId = $targetClass->getIdentifierObject($other); @@ -2028,7 +2028,7 @@ private function doMerge(object $document, array &$visited, ?object $prevManaged if ($prevManagedCopy !== null) { $assocField = $assoc['fieldName']; - $prevClass = $this->dm->getClassMetadata(get_class($prevManagedCopy)); + $prevClass = $this->dm->getClassMetadata($prevManagedCopy::class); if ($assoc['type'] === ClassMetadata::ONE) { $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy); @@ -2129,7 +2129,7 @@ private function doRefresh(object $document, array &$visited): void $visited[$oid] = $document; // mark visited - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); if (! $class->isEmbeddedDocument) { if ($this->getDocumentState($document) !== self::STATE_MANAGED) { @@ -2149,7 +2149,7 @@ private function doRefresh(object $document, array &$visited): void */ private function cascadeRefresh(object $document, array &$visited): void { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $associationMappings = array_filter( $class->associationMappings, @@ -2180,7 +2180,7 @@ private function cascadeRefresh(object $document, array &$visited): void */ private function cascadeDetach(object $document, array &$visited): void { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); foreach ($class->fieldMappings as $mapping) { if (! $mapping['isCascadeDetach']) { continue; @@ -2209,7 +2209,7 @@ private function cascadeDetach(object $document, array &$visited): void */ private function cascadeMerge(object $document, object $managedCopy, array &$visited): void { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $associationMappings = array_filter( $class->associationMappings, @@ -2241,7 +2241,7 @@ private function cascadeMerge(object $document, object $managedCopy, array &$vis */ private function cascadePersist(object $document, array &$visited): void { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $associationMappings = array_filter( $class->associationMappings, @@ -2299,7 +2299,7 @@ private function cascadePersist(object $document, array &$visited): void */ private function cascadeRemove(object $document, array &$visited): void { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); foreach ($class->fieldMappings as $mapping) { if (! $mapping['isCascadeRemove'] && ( ! isset($mapping['orphanRemoval']) || ! $mapping['orphanRemoval'])) { continue; @@ -2335,7 +2335,7 @@ public function lock(object $document, int $lockMode, ?int $lockVersion = null): throw new InvalidArgumentException('Document is not MANAGED.'); } - $documentName = get_class($document); + $documentName = $document::class; $class = $this->dm->getClassMetadata($documentName); if ($lockMode === LockMode::OPTIMISTIC) { @@ -2367,7 +2367,7 @@ public function unlock(object $document): void throw new InvalidArgumentException('Document is not MANAGED.'); } - $documentName = get_class($document); + $documentName = $document::class; $this->getDocumentPersister($documentName)->unlock($document); } @@ -2664,7 +2664,7 @@ private function scheduleCollectionOwner(PersistentCollectionInterface $coll): v } if (CollectionHelper::isAtomic($mapping['strategy'])) { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); $atomicCollection = $class->getFieldValue($document, $mapping['fieldName']); $this->scheduleCollectionUpdate($atomicCollection); $this->unscheduleCollectionDeletion($coll); @@ -2690,16 +2690,21 @@ private function scheduleCollectionOwner(PersistentCollectionInterface $coll): v */ public function getOwningDocument(object $document): object { - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); while ($class->isEmbeddedDocument) { $parentAssociation = $this->getParentAssociation($document); if (! $parentAssociation) { - throw new UnexpectedValueException('Could not determine parent association for ' . get_class($document)); + throw new UnexpectedValueException('Could not determine parent association for ' . $document::class); } - [, $document] = $parentAssociation; - $class = $this->dm->getClassMetadata(get_class($document)); + [, $parentDocument] = $parentAssociation; + if (! $parentDocument) { + throw new UnexpectedValueException('Could not determine parent association for ' . $document::class); + } + + $document = $parentDocument; + $class = $this->dm->getClassMetadata($document::class); } return $document; @@ -2961,7 +2966,7 @@ public function size(): int public function registerManaged(object $document, $id, array $data): void { $oid = spl_object_hash($document); - $class = $this->dm->getClassMetadata(get_class($document)); + $class = $this->dm->getClassMetadata($document::class); if (! $class->identifier || $id === null) { $this->documentIdentifiers[$oid] = $oid; @@ -2997,7 +3002,7 @@ public function clearDocumentChangeSet(string $oid): void public function propertyChanged($sender, $propertyName, $oldValue, $newValue) { $oid = spl_object_hash($sender); - $class = $this->dm->getClassMetadata(get_class($sender)); + $class = $this->dm->getClassMetadata($sender::class); if (! isset($class->fieldMappings[$propertyName])) { return; // ignore non-persistent fields @@ -3092,6 +3097,6 @@ public function initializeObject(object $obj): void private function objToStr(object $obj): string { - return method_exists($obj, '__toString') ? (string) $obj : get_class($obj) . '@' . spl_object_hash($obj); + return method_exists($obj, '__toString') ? (string) $obj : $obj::class . '@' . spl_object_hash($obj); } } diff --git a/lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php b/lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php index 8d4f6a8834..46cbf3d17a 100644 --- a/lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php +++ b/lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php @@ -16,22 +16,11 @@ use Doctrine\ODM\MongoDB\PersistentCollection\PersistentCollectionInterface; use Doctrine\ODM\MongoDB\UnitOfWork; -use function get_class; - /** @internal */ final class LifecycleEventManager { - private DocumentManager $dm; - - private EventManager $evm; - - private UnitOfWork $uow; - - public function __construct(DocumentManager $dm, UnitOfWork $uow, EventManager $evm) + public function __construct(private DocumentManager $dm, private UnitOfWork $uow, private EventManager $evm) { - $this->dm = $dm; - $this->evm = $evm; - $this->uow = $uow; } /** @@ -173,7 +162,7 @@ private function cascadePreUpdate(ClassMetadata $class, object $document): void continue; } - $this->preUpdate($this->dm->getClassMetadata(get_class($entry)), $entry); + $this->preUpdate($this->dm->getClassMetadata($entry::class), $entry); } } } @@ -201,7 +190,7 @@ private function cascadePostUpdate(ClassMetadata $class, object $document): void continue; } - $entryClass = $this->dm->getClassMetadata(get_class($entry)); + $entryClass = $this->dm->getClassMetadata($entry::class); $event = $this->uow->isScheduledForInsert($entry) ? Events::postPersist : Events::postUpdate; $entryClass->invokeLifecycleCallbacks($event, $entry, [new LifecycleEventArgs($entry, $this->dm)]); $this->dispatchEvent($entryClass, $event, new LifecycleEventArgs($entry, $this->dm)); @@ -229,7 +218,7 @@ private function cascadePostPersist(ClassMetadata $class, object $document): voi $values = $mapping['type'] === ClassMetadata::ONE ? [$value] : $value; foreach ($values as $embeddedDocument) { - $this->postPersist($this->dm->getClassMetadata(get_class($embeddedDocument)), $embeddedDocument); + $this->postPersist($this->dm->getClassMetadata($embeddedDocument::class), $embeddedDocument); } } } diff --git a/phpcs.xml.dist b/phpcs.xml.dist index dab81f4291..aafaff2fe1 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -9,7 +9,7 @@ - + benchmark lib @@ -43,6 +43,9 @@ + + + diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index c3fa31dd1e..199b8e5584 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,244 +1,1246 @@ parameters: - ignoreErrors: - # Adding a parameter would be BC-break - - - message: "#^PHPDoc tag @param references unknown parameter\\: \\$applyFilters$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php - - # Making classes final as suggested would be a BC-break - - - message: "#^Unsafe usage of new static\\(\\)\\.$#" - paths: - - lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php - - lib/Doctrine/ODM/MongoDB/DocumentManager.php - - # This cannot be solved the way it is, see https://github.com/vimeo/psalm/issues/5788 - - - message: "#^Return type \\(Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/DocumentManager.php - - # The limit option in GeoNear has been removed in MongoDB 4.2 in favor of $limit stage - - - message: "#^Return type \\(Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\GeoNear\\) of method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\GeoNear\\:\\:limit\\(\\) should be compatible with return type \\(Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Limit\\) of method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\:\\:limit\\(\\)$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GeoNear.php - - - - message: "#DOCTRINE_MONGODB_DATABASE not found\\.$#" - paths: - - tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php - - tests/Doctrine/ODM/MongoDB/Tests/DocumentRepositoryTest.php - - tests/Doctrine/ODM/MongoDB/Tests/Id/IncrementGeneratorTest.php - - tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php - - - - message: "#DOCTRINE_MONGODB_SERVER not found\\.$#" - paths: - - tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php - - - - message: "#^Parameter \\#1 \\$builder of method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Facet\\:\\:pipeline\\(\\) expects Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Builder\\|Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage, stdClass given\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php - - - - message: "#^Expression \"\\$groups\\[0\\]\" on a separate line does not do anything\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Functional/FunctionalTest.php - - - - message: "#^Unreachable statement \\- code above always terminates\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH580Test.php - - - - message: "#^Parameter \\#1 \\$primer of method Doctrine\\\\ODM\\\\MongoDB\\\\Query\\\\Builder\\:\\:prime\\(\\) expects bool\\|\\(callable\\(\\)\\: mixed\\), 1 given\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php - - # Support for doctrine/collections v1 - - - message: '#^Method Doctrine\\ODM\\MongoDB\\PersistentCollection\:\:add\(\) with return type void returns true but should not return anything\.$#' - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # import type in PHPStan does not work, see https://github.com/phpstan/phpstan/issues/5091 - - - message: "#^Access to offset '.+' on an unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\.$#" - count: 12 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # import type in PHPStan does not work, see https://github.com/phpstan/phpstan/issues/5091 - - - message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:getMapping\\(\\) should return array\\{type\\: string, fieldName\\: string, name\\: string, isCascadeRemove\\: bool, isCascadePersist\\: bool, isCascadeRefresh\\: bool, isCascadeMerge\\: bool, isCascadeDetach\\: bool, \\.\\.\\.\\} but returns Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\|null\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # import type in PHPStan does not work, see https://github.com/phpstan/phpstan/issues/5091 - - - message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:\\$mapping has unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping as its type\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # import type in PHPStan does not work, see https://github.com/phpstan/phpstan/issues/5091 - - - message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\:\\:\\$mapping \\(Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\|null\\) does not accept array\\\\|bool\\|int\\|string\\|null\\>\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # That statement is never reached because DateTimeInterface is either DateTimeImmutable or DateTime - - - message: "#^Unreachable statement \\- code above always terminates\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Types/DateImmutableType.php - - # These classes are not final - - - message: "#^Unsafe call to private method Doctrine\\\\ODM\\\\MongoDB\\\\Query\\\\Expr\\:\\:convertExpression\\(\\) through static::\\.$#" - count: 3 - path: lib/Doctrine/ODM/MongoDB/Query/Expr.php - - - - message: "#^Unsafe call to private method Doctrine\\\\ODM\\\\MongoDB\\\\Types\\\\DateType\\:\\:craftDateTime\\(\\) through static::\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Types/DateType.php - - # False positive, the exception is thrown - - - message: "#^Dead catch \\- MongoDB\\\\Driver\\\\Exception\\\\BulkWriteException is never thrown in the try block\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH580Test.php - - # Properties are not covariant - - - message: "#^PHPDoc type Doctrine\\\\Common\\\\Collections\\\\Collection\\ of property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocumentWithDiscriminator\\:\\:\\$embeddedChildren is not covariant with PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of overridden property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocument\\:\\:\\$embeddedChildren\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DiscriminatorsDefaultValueTest.php - - - - message: "#^PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocumentWithDiscriminator\\:\\:\\$referencedChildren is not covariant with PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of overridden property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocument\\:\\:\\$referencedChildren\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DiscriminatorsDefaultValueTest.php - - - - message: "#^PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocumentWithoutDiscriminator\\:\\:\\$embeddedChildren is not covariant with PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of overridden property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocument\\:\\:\\$embeddedChildren\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DiscriminatorsDefaultValueTest.php - - - - message: "#^PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocumentWithoutDiscriminator\\:\\:\\$referencedChildren is not covariant with PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of overridden property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocument\\:\\:\\$referencedChildren\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DiscriminatorsDefaultValueTest.php - - # Collection elements cannot be covariant, see https://github.com/doctrine/collections/pull/220 - - - message: "#^Parameter \\#2 \\$projects of class Documents\\\\Developer constructor expects Doctrine\\\\Common\\\\Collections\\\\Collection\\\\|null, Doctrine\\\\Common\\\\Collections\\\\ArrayCollection\\ given\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/DocumentRepositoryTest.php - - # When iterating over SimpleXMLElement, we cannot know the key values - - - message: "#^Parameter \\#2 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\Driver\\\\XmlDriver\\:\\:addFieldMapping\\(\\) expects array#" - count: 2 - path: lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php - - - - message: "#^Parameter \\#2 \\$options of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\\\:\\:addIndex\\(\\) expects array\\{background\\?\\: bool, bits\\?\\: int, default_language\\?\\: string, expireAfterSeconds\\?\\: int, language_override\\?\\: string, min\\?\\: float, max\\?\\: float, name\\?\\: string, \\.\\.\\.\\}, array\\\\|string, mixed\\>\\|bool\\|float\\|int\\|string\\|null\\>\\|bool\\|float\\|int\\|string\\|null\\> given\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php - - # This is handled by a try-catch block - - - message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" - paths: - - lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php - - lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php - - lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php - - # $this->mapping['targetDocument'] is class-string - - - message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # complains about types for arguments we do not use/care - - - message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Proxy\\\\Factory\\\\StaticProxyFactory\\:\\:createInitializer\\(\\) should return Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface\\&TDocument of object\\=, string\\=, array\\\\=, Closure\\|null\\=, array\\\\=\\)\\: bool but returns Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface, string, array, mixed, array\\)\\: true\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php - - - - message: "#^Parameter \\#1 \\$initializer of method ProxyManager\\\\Proxy\\\\GhostObjectInterface\\\\:\\:setProxyInitializer\\(\\) expects \\(Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface\\\\=, string\\=, array\\\\=, Closure\\|null\\=, array\\\\=\\)\\: bool\\)\\|null, Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface, string, array, mixed, array\\)\\: true given\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php - - # compatibility layer for doctrine/persistence ^2.4 || ^3.0 - - - message: "#.*#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/Event/OnClearEventArgs - - - - message: "#Property .+ is never written, only read.#" - path: tests - - - - message: "#Property .+ is never read, only written.#" - path: tests - - - - message: "#Property .+ is unused.#" - path: tests - - - - message: "#^Parameter \\#4 \\$query of class Doctrine\\\\ODM\\\\MongoDB\\\\Query\\\\Query constructor expects array\\{distinct\\?\\: string, hint\\?\\: array\\\\|string, limit\\?\\: int, maxTimeMS\\?\\: int, multiple\\?\\: bool, new\\?\\: bool, newObj\\?\\: array\\, query\\?\\: array\\, \\.\\.\\.\\}, array\\{type\\: \\-1\\} given\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php - - - - message: "#^Parameter \\#2 \\$referenceMapping of method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:createReference\\(\\) expects array\\{type\\: string, fieldName\\: string, name\\: string, isCascadeRemove\\: bool, isCascadePersist\\: bool, isCascadeRefresh\\: bool, isCascadeMerge\\: bool, isCascadeDetach\\: bool, \\.\\.\\.\\}, array\\{storeAs\\: 'dbRef'\\} given\\.$#" - count: 1 - path: tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php - - # import type in PHPStan does not work, see https://github.com/phpstan/phpstan/issues/5091 - - - message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:\\$hints \\(Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\Hints\\) does not accept default value of type array\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # import type in PHPStan does not work, see https://github.com/phpstan/phpstan/issues/5091 - - - message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:\\$hints has unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\Hints as its type\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # import type in PHPStan does not work, see https://github.com/phpstan/phpstan/issues/5091 - - - message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\:\\:\\$hints \\(Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\Hints\\) does not accept array\\\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # import type in PHPStan does not work, see https://github.com/phpstan/phpstan/issues/5091 - - - message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:getHints\\(\\) should return array\\ but returns Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\Hints\\.$#" - count: 1 - path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php - - # breaking types expected by static analysis to check exceptions - - - message: "#.+mapField\\(\\) expects.+enumType\\: 'Documents81#" - count: 2 - path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php - - - - message: "#has parameter \\$[^\\s]+ with no value type specified in iterable type array\\.$#" - count: 6 - path: tests/* - - # cannot know that Command::getHelper('documentManager') returns a DocumentManagerHelper - - - message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:getDocumentManager\\(\\)\\.$#" - count: 7 - path: lib/Doctrine/ODM/MongoDB/Tools/Console/Command/* + ignoreErrors: + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$applyFilters$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Builder.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Expr.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\AbstractReplace\\:\\:getReplaceExpression\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/AbstractReplace.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Bucket\\\\AbstractOutput\\:\\:avg\\(\\) has parameter \\$expressions with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Bucket\\\\AbstractOutput\\:\\:max\\(\\) has parameter \\$expressions with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Bucket\\\\AbstractOutput\\:\\:mergeObjects\\(\\) has parameter \\$expressions with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Bucket\\\\AbstractOutput\\:\\:min\\(\\) has parameter \\$expressions with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Bucket\\\\AbstractOutput\\:\\:stdDevPop\\(\\) has parameter \\$expressions with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Bucket\\\\AbstractOutput\\:\\:stdDevSamp\\(\\) has parameter \\$expressions with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Bucket\\\\AbstractOutput\\:\\:sum\\(\\) has parameter \\$expressions with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Bucket/AbstractOutput.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Densify\\:\\:getExpression\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Densify.php + + - + message: "#^Return type \\(static\\(Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\GeoNear\\)\\) of method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\GeoNear\\:\\:limit\\(\\) should be compatible with return type \\(Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Limit\\) of method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\:\\:limit\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GeoNear.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Lookup\\:\\:getExpression\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Lookup.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Merge\\:\\:getExpression\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Merge.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Out.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\:\\:getExpression\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedAutocomplete\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedAutocomplete.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedAutocomplete\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedAutocomplete.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedAutocomplete\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedAutocomplete.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedEmbeddedDocument\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedEmbeddedDocument.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedEmbeddedDocument\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedEmbeddedDocument.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedEmbeddedDocument\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedEmbeddedDocument.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedEquals\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedEquals.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedEquals\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedEquals.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedEquals\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedEquals.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedExists\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedExists.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedExists\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedExists.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedExists\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedExists.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedGeoShape\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedGeoShape.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedGeoShape\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedGeoShape.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedGeoShape\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedGeoShape.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedGeoWithin\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedGeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedGeoWithin\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedGeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedGeoWithin\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedGeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedMoreLikeThis\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedMoreLikeThis.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedMoreLikeThis\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedMoreLikeThis.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedMoreLikeThis\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedMoreLikeThis.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedNear\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedNear.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedNear\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedNear.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedNear\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedNear.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedPhrase\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedPhrase.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedPhrase\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedPhrase.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedPhrase\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedPhrase.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedQueryString\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedQueryString.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedQueryString\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedQueryString.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedQueryString\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedQueryString.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedRange\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedRange.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedRange\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedRange.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedRange\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedRange.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedRegex\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedRegex.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedRegex\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedRegex.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedRegex\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedRegex.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedText\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedText.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedText\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedText.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedText\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedText.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedWildcard\\:\\:__construct\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedWildcard.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedWildcard\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedWildcard.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Compound\\\\CompoundedWildcard\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Compound/CompoundedWildcard.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\EmbeddedDocument\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/EmbeddedDocument.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\EmbeddedDocument\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/EmbeddedDocument.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoShape\\:\\:__construct\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoShape.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoShape\\:\\:geometry\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoShape.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoShape\\:\\:\\$geometry type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoShape.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoWithin\\:\\:box\\(\\) has parameter \\$bottomLeft with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoWithin\\:\\:box\\(\\) has parameter \\$topRight with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoWithin\\:\\:circle\\(\\) has parameter \\$center with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoWithin\\:\\:convertGeometry\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoWithin\\:\\:convertGeometry\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoWithin\\:\\:geometry\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoWithin\\:\\:\\$geometry type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\GeoWithin\\:\\:\\$relation is never read, only written\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/GeoWithin.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Near\\:\\:__construct\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Near.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Near\\:\\:origin\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Near.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Near\\:\\:\\$origin type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Near.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\Range\\:\\:\\$includeUpperBound is never read, only written\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/Range.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\SupportsGeoShapeOperator\\:\\:geoShape\\(\\) has parameter \\$geometry with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsGeoShapeOperator.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Search\\\\SupportsNearOperator\\:\\:near\\(\\) has parameter \\$origin with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Search/SupportsNearOperator.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\UnionWith\\:\\:getExpression\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\UnionWith\\:\\:pipeline\\(\\) has parameter \\$pipeline with no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\UnionWith\\:\\:\\$pipeline type has no value type specified in iterable type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Aggregation/Stage/UnionWith.php + + - + message: "#^Return type \\(Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadataFactory\\) of method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getMetadataFactory\\(\\) should be compatible with return type \\(Doctrine\\\\Persistence\\\\Mapping\\\\ClassMetadataFactory\\\\>\\) of method Doctrine\\\\Persistence\\\\ObjectManager\\:\\:getMetadataFactory\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/DocumentManager.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/DocumentManager.php + + - + message: "#^Method Doctrine\\\\Persistence\\\\Event\\\\OnClearEventArgs\\\\:\\:__construct\\(\\) invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Event/OnClearEventArgs.php + + - + message: "#^Parameter \\#1 \\$initializer of method ProxyManager\\\\Proxy\\\\GhostObjectInterface\\\\:\\:setProxyInitializer\\(\\) expects \\(Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface\\\\=, string\\=, array\\\\=, Closure\\|null\\=, array\\\\=\\)\\: bool\\)\\|null, Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface, string, array, mixed, array\\)\\: true given\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Hydrator/HydratorFactory.php + + - + message: "#^Parameter \\#2 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\Driver\\\\XmlDriver\\:\\:addFieldMapping\\(\\) expects array\\{type\\?\\: string, fieldName\\?\\: string, name\\?\\: string, strategy\\?\\: string, association\\?\\: int, id\\?\\: bool, isOwningSide\\?\\: bool, collectionClass\\?\\: class\\-string, \\.\\.\\.\\}, array\\\\|bool\\|string\\> given\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php + + - + message: "#^Parameter \\#2 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\Driver\\\\XmlDriver\\:\\:addFieldMapping\\(\\) expects array\\{type\\?\\: string, fieldName\\?\\: string, name\\?\\: string, strategy\\?\\: string, association\\?\\: int, id\\?\\: bool, isOwningSide\\?\\: bool, collectionClass\\?\\: class\\-string, \\.\\.\\.\\}, non\\-empty\\-array\\\\|string\\|true\\> given\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php + + - + message: "#^Parameter \\#2 \\$options of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\\\:\\:addIndex\\(\\) expects array\\{background\\?\\: bool, bits\\?\\: int, default_language\\?\\: string, expireAfterSeconds\\?\\: int, language_override\\?\\: string, min\\?\\: float, max\\?\\: float, name\\?\\: string, \\.\\.\\.\\}, array\\\\|string, mixed\\>\\|bool\\|float\\|int\\|string\\|null\\>\\|bool\\|float\\|int\\|string\\|null\\> given\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Mapping/Driver/XmlDriver.php + + - + message: "#^Access to offset 'embedded' on an unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Access to offset 'isOwningSide' on an unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\.$#" + count: 3 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Access to offset 'orphanRemoval' on an unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Access to offset 'reference' on an unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Access to offset 'strategy' on an unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\.$#" + count: 4 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Access to offset 'targetDocument' on an unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\.$#" + count: 2 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:add\\(\\) with return type void returns true but should not return anything\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:getHints\\(\\) should return array\\ but returns Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\Hints\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:getMapping\\(\\) should return array\\{type\\: string, fieldName\\: string, name\\: string, isCascadeRemove\\: bool, isCascadePersist\\: bool, isCascadeRefresh\\: bool, isCascadeMerge\\: bool, isCascadeDetach\\: bool, \\.\\.\\.\\} but returns Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\|null\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:\\$hints \\(Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\Hints\\) does not accept default value of type array\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:\\$hints has unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\Hints as its type\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\:\\:\\$mapping has unknown class Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping as its type\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\:\\:\\$hints \\(Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\Hints\\) does not accept array\\\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\:\\:\\$mapping \\(Doctrine\\\\ODM\\\\MongoDB\\\\PersistentCollection\\\\FieldMapping\\|null\\) does not accept array\\\\|bool\\|int\\|string\\|null\\>\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/PersistentCollection.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Proxy\\\\Factory\\\\StaticProxyFactory\\:\\:createInitializer\\(\\) should return Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface\\&TDocument of object\\=, string\\=, array\\\\=, Closure\\|null\\=, array\\\\=\\)\\: bool but returns Closure\\(ProxyManager\\\\Proxy\\\\GhostObjectInterface, string, array, mixed, array\\)\\: true\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Proxy/Factory/StaticProxyFactory.php + + - + message: "#^Unsafe call to private method Doctrine\\\\ODM\\\\MongoDB\\\\Query\\\\Expr\\:\\:convertExpression\\(\\) through static\\:\\:\\.$#" + count: 3 + path: lib/Doctrine/ODM/MongoDB/Query/Expr.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:getDocumentManager\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Tools/Console/Command/ClearCache/MetadataCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:getDocumentManager\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateHydratorsCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:getDocumentManager\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GeneratePersistentCollectionsCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:getDocumentManager\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Tools/Console/Command/GenerateProxiesCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:getDocumentManager\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Tools/Console/Command/QueryCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:getDocumentManager\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/AbstractCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:getDocumentManager\\(\\)\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Tools/Console/Command/Schema/ValidateCommand.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Types/DateImmutableType.php + + - + message: "#^Unsafe call to private method Doctrine\\\\ODM\\\\MongoDB\\\\Types\\\\DateType\\:\\:craftDateTime\\(\\) through static\\:\\:\\.$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/Types/DateType.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" + count: 1 + path: lib/Doctrine/ODM/MongoDB/UnitOfWork.php + + - + message: "#^Unable to resolve the template type T in call to method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:getClassMetadata\\(\\)$#" + count: 3 + path: lib/Doctrine/ODM/MongoDB/Utility/LifecycleEventManager.php + + - + message: "#^Parameter \\#1 \\$builder of method Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage\\\\Facet\\:\\:pipeline\\(\\) expects Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Builder\\|Doctrine\\\\ODM\\\\MongoDB\\\\Aggregation\\\\Stage, stdClass given\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Aggregation\\\\Stage\\\\ProjectTest\\:\\:testAccumulatorsWithMultipleArguments\\(\\) has parameter \\$args with no type specified\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ProjectTest.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Aggregation\\\\Stage\\\\ProjectTest\\:\\:testAccumulatorsWithMultipleArguments\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ProjectTest.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Aggregation\\\\Stage\\\\SearchTest\\:\\:testSearchCompoundOperators\\(\\) has parameter \\$expectedOperator with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SearchTest.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Aggregation\\\\Stage\\\\SearchTest\\:\\:testSearchEmbeddedDocumentOperators\\(\\) has parameter \\$expectedOperator with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SearchTest.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Aggregation\\\\Stage\\\\SearchTest\\:\\:testSearchOperators\\(\\) has parameter \\$expectedOperator with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SearchTest.php + + - + message: "#^Constant DOCTRINE_MONGODB_DATABASE not found\\.$#" + count: 5 + path: tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php + + - + message: "#^Constant DOCTRINE_MONGODB_SERVER not found\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\BaseTestCase\\:\\:assertArraySubset\\(\\) has parameter \\$array with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\BaseTestCase\\:\\:assertArraySubset\\(\\) has parameter \\$subset with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php + + - + message: "#^Used constant DOCTRINE_MONGODB_DATABASE not found\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php + + - + message: "#^Used constant DOCTRINE_MONGODB_SERVER not found\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/BaseTestCase.php + + - + message: "#^Parameter \\#2 \\$referenceMapping of method Doctrine\\\\ODM\\\\MongoDB\\\\DocumentManager\\:\\:createReference\\(\\) expects array\\{type\\: string, fieldName\\: string, name\\: string, isCascadeRemove\\: bool, isCascadePersist\\: bool, isCascadeRefresh\\: bool, isCascadeMerge\\: bool, isCascadeDetach\\: bool, \\.\\.\\.\\}, array\\{storeAs\\: 'dbRef'\\} given\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php + + - + message: "#^Constant DOCTRINE_MONGODB_DATABASE not found\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/DocumentRepositoryTest.php + + - + message: "#^Parameter \\#2 \\$projects of class Documents\\\\Developer constructor expects Doctrine\\\\Common\\\\Collections\\\\Collection\\\\|null, Doctrine\\\\Common\\\\Collections\\\\ArrayCollection\\ given\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/DocumentRepositoryTest.php + + - + message: "#^Used constant DOCTRINE_MONGODB_DATABASE not found\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/DocumentRepositoryTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\CustomDatabaseTest\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DatabasesTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\DefaultDatabaseTest\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DatabasesTest.php + + - + message: "#^PHPDoc type Doctrine\\\\Common\\\\Collections\\\\Collection\\ of property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocumentWithDiscriminator\\:\\:\\$embeddedChildren is not covariant with PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of overridden property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocument\\:\\:\\$embeddedChildren\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DiscriminatorsDefaultValueTest.php + + - + message: "#^PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocumentWithDiscriminator\\:\\:\\$referencedChildren is not covariant with PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of overridden property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocument\\:\\:\\$referencedChildren\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DiscriminatorsDefaultValueTest.php + + - + message: "#^PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocumentWithoutDiscriminator\\:\\:\\$embeddedChildren is not covariant with PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of overridden property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocument\\:\\:\\$embeddedChildren\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DiscriminatorsDefaultValueTest.php + + - + message: "#^PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocumentWithoutDiscriminator\\:\\:\\$referencedChildren is not covariant with PHPDoc type array\\\\|Doctrine\\\\Common\\\\Collections\\\\Collection\\ of overridden property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentDocument\\:\\:\\$referencedChildren\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DiscriminatorsDefaultValueTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\DocumentPersisterTestDocumentWithReferenceToDocumentWithCustomId\\:\\:\\$documentWithCustomId is never read, only written\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\DocumentPersisterTestDocumentWithReferenceToDocumentWithCustomId\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php + + - + message: "#^Expression \"\\$groups\\[0\\]\" on a separate line does not do anything\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/FunctionalTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ChildObject\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/LifecycleTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\ParentObject\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/LifecycleTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Hierarchy\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedDocumentsTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH1058PersistDocument\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1058Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH1058PersistDocument\\:\\:\\$value is never read, only written\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1058Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH1058UpsertDocument\\:\\:\\$value is never read, only written\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1058Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH1964Document\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1964Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH1990Document\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1990Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH1990Document\\:\\:\\$parent is never read, only written\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1990Test.php + + - + message: "#^Dead catch \\- MongoDB\\\\Driver\\\\Exception\\\\BulkWriteException is never thrown in the try block\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH580Test.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH580Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH921Post\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH921Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH921User\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH921Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\GH999Document\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH999Test.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Functional\\\\Ticket\\\\MODM116Parent\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php + + - + message: "#^Constant DOCTRINE_MONGODB_DATABASE not found\\.$#" + count: 2 + path: tests/Doctrine/ODM/MongoDB/Tests/Id/IncrementGeneratorTest.php + + - + message: "#^Used constant DOCTRINE_MONGODB_DATABASE not found\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Id/IncrementGeneratorTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\AnnotationDriverTestSuper\\:\\:\\$private is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractAnnotationDriverTestCase.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\DocumentSubClass2\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\DocumentSubClass2\\:\\:\\$name is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\DocumentSubClass\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\DocumentSubClass\\:\\:\\$name is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\GridFSChildClass\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\GridFSParentClass\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\MappedSuperclassBase\\:\\:\\$mapped1 is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\MappedSuperclassBase\\:\\:\\$mapped2 is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\MappedSuperclassBase\\:\\:\\$mappedRelated1 is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\MappedSuperclassBase\\:\\:\\$transient is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\TransientBaseClass\\:\\:\\$transient1 is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\TransientBaseClass\\:\\:\\$transient2 is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/BasicInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\LoadEventTestDocument\\:\\:\\$about is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataLoadEventTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\LoadEventTestDocument\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataLoadEventTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\LoadEventTestDocument\\:\\:\\$name is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataLoadEventTest.php + + - + message: "#^Parameter \\#1 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\\\:\\:mapField\\(\\) expects array\\{type\\?\\: string, fieldName\\?\\: string, name\\?\\: string, strategy\\?\\: string, association\\?\\: int, id\\?\\: bool, isOwningSide\\?\\: bool, collectionClass\\?\\: class\\-string, \\.\\.\\.\\}, array\\{fieldName\\: 'enum', enumType\\: 'Documents81\\\\\\\\Card'\\} given\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php + + - + message: "#^Parameter \\#1 \\$mapping of method Doctrine\\\\ODM\\\\MongoDB\\\\Mapping\\\\ClassMetadata\\\\:\\:mapField\\(\\) expects array\\{type\\?\\: string, fieldName\\?\\: string, name\\?\\: string, strategy\\?\\: string, association\\?\\: int, id\\?\\: bool, isOwningSide\\?\\: bool, collectionClass\\?\\: class\\-string, \\.\\.\\.\\}, array\\{fieldName\\: 'enum', enumType\\: 'Documents81\\\\\\\\SuitNonBacked'\\} given\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php + + - + message: "#^Property DoctrineGlobal_User\\:\\:\\$email is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/Documents/GlobalNamespaceDocument.php + + - + message: "#^Property DoctrineGlobal_User\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/Documents/GlobalNamespaceDocument.php + + - + message: "#^Property DoctrineGlobal_User\\:\\:\\$username is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/Documents/GlobalNamespaceDocument.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\ShardedCollectionPerClass1\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ShardKeyInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\ShardedSingleCollInheritance1\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ShardKeyInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\ShardedSubclass\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ShardKeyInheritanceMappingTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Mapping\\\\ShardedSuperclass\\:\\:\\$name is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Mapping/ShardKeyInheritanceMappingTest.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Query\\\\BuilderTest\\:\\:testExclude\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Query\\\\BuilderTest\\:\\:testProxiedExprMethods\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php + + - + message: "#^Method Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Query\\\\BuilderTest\\:\\:testSelect\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php + + - + message: "#^Parameter \\#1 \\$primer of method Doctrine\\\\ODM\\\\MongoDB\\\\Query\\\\Builder\\:\\:prime\\(\\) expects bool\\|\\(callable\\(\\)\\: mixed\\), 1 given\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Query/BuilderTest.php + + - + message: "#^Constant DOCTRINE_MONGODB_DATABASE not found\\.$#" + count: 2 + path: tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php + + - + message: "#^Parameter \\#4 \\$query of class Doctrine\\\\ODM\\\\MongoDB\\\\Query\\\\Query constructor expects array\\{distinct\\?\\: string, hint\\?\\: array\\\\|string, limit\\?\\: int, maxTimeMS\\?\\: int, multiple\\?\\: bool, new\\?\\: bool, newObj\\?\\: array\\, query\\?\\: array\\, \\.\\.\\.\\}, array\\{type\\: \\-1\\} given\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php + + - + message: "#^Used constant DOCTRINE_MONGODB_DATABASE not found\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/QueryTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Tools\\\\GH297\\\\User\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Tools/GH297/User.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Tools\\\\ResolveTargetDocument\\:\\:\\$embedMany is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Tools/ResolveTargetDocumentListenerTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Tools\\\\ResolveTargetDocument\\:\\:\\$embedOne is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Tools/ResolveTargetDocumentListenerTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Tools\\\\ResolveTargetDocument\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Tools/ResolveTargetDocumentListenerTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Tools\\\\ResolveTargetDocument\\:\\:\\$refMany is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Tools/ResolveTargetDocumentListenerTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Tools\\\\ResolveTargetDocument\\:\\:\\$refOne is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Tools/ResolveTargetDocumentListenerTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\Tools\\\\TargetDocument\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/Tools/ResolveTargetDocumentListenerTest.php + + - + message: "#^Property Doctrine\\\\ODM\\\\MongoDB\\\\Tests\\\\ArrayTest\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php + + - + message: "#^Property Documents\\\\Account\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Account.php + + - + message: "#^Property Documents\\\\Address\\:\\:\\$test is unused\\.$#" + count: 1 + path: tests/Documents/Address.php + + - + message: "#^Property Documents\\\\Album\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Album.php + + - + message: "#^Property Documents\\\\Article\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Article.php + + - + message: "#^Property Documents\\\\Bars\\\\Bar\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Bars/Bar.php + + - + message: "#^Property Documents\\\\Category\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Documents/Category.php + + - + message: "#^Property Documents\\\\Developer\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Developer.php + + - + message: "#^Property Documents\\\\Developer\\:\\:\\$name is never read, only written\\.$#" + count: 1 + path: tests/Documents/Developer.php + + - + message: "#^Property Documents\\\\Ecommerce\\\\StockItem\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Ecommerce/StockItem.php + + - + message: "#^Property Documents\\\\Event\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Event.php + + - + message: "#^Property Documents\\\\File\\:\\:\\$chunkSize is never written, only read\\.$#" + count: 1 + path: tests/Documents/File.php + + - + message: "#^Property Documents\\\\File\\:\\:\\$filename is never written, only read\\.$#" + count: 1 + path: tests/Documents/File.php + + - + message: "#^Property Documents\\\\File\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/File.php + + - + message: "#^Property Documents\\\\File\\:\\:\\$length is never written, only read\\.$#" + count: 1 + path: tests/Documents/File.php + + - + message: "#^Property Documents\\\\File\\:\\:\\$uploadDate is never written, only read\\.$#" + count: 1 + path: tests/Documents/File.php + + - + message: "#^Property Documents\\\\FileWithoutChunkSize\\:\\:\\$chunkSize is never written, only read\\.$#" + count: 1 + path: tests/Documents/FileWithoutChunkSize.php + + - + message: "#^Property Documents\\\\FileWithoutChunkSize\\:\\:\\$filename is never written, only read\\.$#" + count: 1 + path: tests/Documents/FileWithoutChunkSize.php + + - + message: "#^Property Documents\\\\FileWithoutChunkSize\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/FileWithoutChunkSize.php + + - + message: "#^Property Documents\\\\FileWithoutChunkSize\\:\\:\\$length is never written, only read\\.$#" + count: 1 + path: tests/Documents/FileWithoutChunkSize.php + + - + message: "#^Property Documents\\\\FileWithoutChunkSize\\:\\:\\$uploadDate is never written, only read\\.$#" + count: 1 + path: tests/Documents/FileWithoutChunkSize.php + + - + message: "#^Property Documents\\\\FileWithoutMetadata\\:\\:\\$filename is never written, only read\\.$#" + count: 1 + path: tests/Documents/FileWithoutMetadata.php + + - + message: "#^Property Documents\\\\FileWithoutMetadata\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/FileWithoutMetadata.php + + - + message: "#^Property Documents\\\\Functional\\\\FavoritesUser\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Functional/FavoritesUser.php + + - + message: "#^Property Documents\\\\Group\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Group.php + + - + message: "#^Property Documents\\\\Message\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Message.php + + - + message: "#^Property Documents\\\\ProfileNotify\\:\\:\\$profileId is never written, only read\\.$#" + count: 1 + path: tests/Documents/ProfileNotify.php + + - + message: "#^Property Documents\\\\Project\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Project.php + + - + message: "#^Property Documents\\\\SchemaValidated\\:\\:\\$email is unused\\.$#" + count: 1 + path: tests/Documents/SchemaValidated.php + + - + message: "#^Property Documents\\\\SchemaValidated\\:\\:\\$id is unused\\.$#" + count: 1 + path: tests/Documents/SchemaValidated.php + + - + message: "#^Property Documents\\\\SchemaValidated\\:\\:\\$name is unused\\.$#" + count: 1 + path: tests/Documents/SchemaValidated.php + + - + message: "#^Property Documents\\\\SchemaValidated\\:\\:\\$phone is unused\\.$#" + count: 1 + path: tests/Documents/SchemaValidated.php + + - + message: "#^Property Documents\\\\SchemaValidated\\:\\:\\$status is unused\\.$#" + count: 1 + path: tests/Documents/SchemaValidated.php + + - + message: "#^Property Documents\\\\Task\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Task.php + + - + message: "#^Property Documents\\\\Tournament\\\\Participant\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Tournament/Participant.php + + - + message: "#^Property Documents\\\\Tournament\\\\Tournament\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/Tournament/Tournament.php + + - + message: "#^Property Documents\\\\UserName\\:\\:\\$id is never written, only read\\.$#" + count: 1 + path: tests/Documents/UserName.php + + - + message: "#^Property Documents\\\\UserName\\:\\:\\$username is never written, only read\\.$#" + count: 1 + path: tests/Documents/UserName.php + + - + message: "#^Property Documents\\\\UserName\\:\\:\\$viewReference is never written, only read\\.$#" + count: 1 + path: tests/Documents/UserName.php + + - + message: "#^Property Documents\\\\ViewReference\\:\\:\\$referenceOneViewMappedBy is never written, only read\\.$#" + count: 1 + path: tests/Documents/ViewReference.php diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 89669c1ce2..1d201d5492 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -19,6 +19,10 @@ ./lib/Doctrine/ODM/MongoDB + + ./lib/Doctrine/ODM/MongoDB/Aggregation/Stage/GraphLookup/Match.php + ./lib/Doctrine/ODM/MongoDB/Aggregation/Stage/Match.php + diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 439b6dee21..3232d06713 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -5,11 +5,6 @@ IteratorAggregate - - - $fields - - implementsInterface(GridFSRepository::class)]]> @@ -260,6 +255,16 @@ + + + Operator + + + + + $documentPersister + + $mapping + $defaultFieldMapping @@ -283,83 +288,6 @@ new ArrayCollection([$project]) - - - friends]]> - friends]]> - friends]]> - friends]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - inception]]> - phonebooks]]> - phonebooks]]> - phonebooks]]> - phonebooks]]> - phonebooks]]> - phonebooks]]> - phonenumbers]]> - phonenumbers]]> - phonenumbers]]> - phonenumbers]]> - phonenumbers]]> - phonenumbers]]> - phonenumbers]]> - phonenumbers]]> - phonenumbersArray]]> - phonenumbersArray]]> - phonenumbersArray]]> - phonenumbersArray]]> - phonenumbersArray]]> - clear - - categories[0]->children, $user->categories[1]->children]]]> @@ -367,12 +295,6 @@ categories[0]->children[0]->children, $user->categories[0]->children[1]->children]]]> - - - $phonenumbers - $phonenumbers - - $i @@ -380,10 +302,6 @@ $j $j - - coll]]> - coll]]> - @@ -423,12 +341,6 @@ embeds]]> - - - embeds]]> - embeds]]> - - assertIsInt @@ -468,12 +380,6 @@ new ArrayCollection([$referenceMany1, $referenceMany2]) - - - refMany]]> - refMany]]> - - $collection diff --git a/psalm.xml b/psalm.xml.dist similarity index 99% rename from psalm.xml rename to psalm.xml.dist index e89edfc049..ad6394f3ed 100644 --- a/psalm.xml +++ b/psalm.xml.dist @@ -4,6 +4,7 @@ findUnusedBaselineEntry="true" findUnusedCode="false" findUnusedPsalmSuppress="true" + phpVersion="8.1" resolveFromConfigFile="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="https://getpsalm.org/schema/config" diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/AggregationOperatorsProviderTrait.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/AggregationOperatorsProviderTrait.php index b9a1c4bcb5..e1f58d85a9 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/AggregationOperatorsProviderTrait.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/AggregationOperatorsProviderTrait.php @@ -8,736 +8,1442 @@ use Doctrine\ODM\MongoDB\Aggregation\Expr; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; use Documents\User; +use Generator; use InvalidArgumentException; use function is_array; trait AggregationOperatorsProviderTrait { - public static function provideAllOperators(): array + public static function provideAccumulatorExpressionOperators(): Generator { - return static::provideAccumulationOperators() + static::provideExpressionOperators(); - } + yield 'avg' => [ + 'expected' => ['$avg' => ['$field1', '$field2']], + 'operator' => 'avg', + 'args' => ['$field1', '$field2'], + ]; - public static function provideAccumulationOperators(): array - { - return [ - 'addToSet' => [ - 'expected' => ['$addToSet' => '$field'], - 'operator' => 'addToSet', - 'args' => ['$field'], - ], - 'avg' => [ - 'expected' => ['$avg' => '$field'], - 'operator' => 'avg', - 'args' => ['$field'], - ], - 'first' => [ - 'expected' => ['$first' => '$field'], - 'operator' => 'first', - 'args' => ['$field'], - ], - 'last' => [ - 'expected' => ['$last' => '$field'], - 'operator' => 'last', - 'args' => ['$field'], - ], - 'max' => [ - 'expected' => ['$max' => '$field'], - 'operator' => 'max', - 'args' => ['$field'], - ], - 'min' => [ - 'expected' => ['$min' => '$field'], - 'operator' => 'min', - 'args' => ['$field'], - ], - 'push' => [ - 'expected' => ['$push' => '$field'], - 'operator' => 'push', - 'args' => ['$field'], - ], - 'stdDevPopWithTwoArrays' => [ - 'expected' => ['$stdDevPop' => ['$array1', '$array2']], - 'operator' => 'stdDevPop', - 'args' => ['$array1', '$array2'], - ], - 'stdDevPopWithMultipleArrays' => [ - 'expected' => ['$stdDevPop' => ['$array1', '$array2', '$array3', '$array4']], - 'operator' => 'stdDevPop', - 'args' => ['$array1', '$array2', '$array3', '$array4'], - ], - 'stdDevSampWithTwoArrays' => [ - 'expected' => ['$stdDevSamp' => ['$array1', '$array2']], - 'operator' => 'stdDevSamp', - 'args' => ['$array1', '$array2'], - ], - 'stdDevSampWithMultipleArrays' => [ - 'expected' => ['$stdDevSamp' => ['$array1', '$array2', '$array3', '$array4']], - 'operator' => 'stdDevSamp', - 'args' => ['$array1', '$array2', '$array3', '$array4'], - ], - 'sum' => [ - 'expected' => ['$sum' => '$field'], - 'operator' => 'sum', - 'args' => ['$field'], - ], + yield 'max' => [ + 'expected' => ['$max' => ['$field1', '$field2']], + 'operator' => 'max', + 'args' => ['$field1', '$field2'], + ]; + + yield 'min' => [ + 'expected' => ['$min' => ['$field1', '$field2']], + 'operator' => 'min', + 'args' => ['$field1', '$field2'], + ]; + + yield 'stdDevPop' => [ + 'expected' => ['$stdDevPop' => ['$field1', '$field2']], + 'operator' => 'stdDevPop', + 'args' => ['$field1', '$field2'], + ]; + + yield 'stdDevSamp' => [ + 'expected' => ['$stdDevSamp' => ['$field1', '$field2']], + 'operator' => 'stdDevSamp', + 'args' => ['$field1', '$field2'], + ]; + + yield 'sum' => [ + 'expected' => ['$sum' => ['$field1', '$field2']], + 'operator' => 'sum', + 'args' => ['$field1', '$field2'], ]; } - public static function provideExpressionOperators(): array + public static function provideArithmeticExpressionOperators(): Generator { - return [ - 'abs' => [ + yield 'abs' => [ 'expected' => ['$abs' => '$field'], 'operator' => 'abs', 'args' => ['$field'], - ], - 'addWithTwoArgs' => [ + ]; + + yield 'addWithTwoArgs' => [ 'expected' => ['$add' => [5, '$field']], 'operator' => 'add', 'args' => [5, '$field'], - ], - 'addWithMultipleArgs' => [ + ]; + + yield 'addWithMultipleArgs' => [ 'expected' => ['$add' => [5, '$field', '$otherField', 4.99]], 'operator' => 'add', 'args' => [5, '$field', '$otherField', 4.99], - ], - 'allElementsTrue' => [ - 'expected' => ['$allElementsTrue' => '$field'], - 'operator' => 'allElementsTrue', - 'args' => ['$field'], - ], - 'anyElementTrue' => [ - 'expected' => ['$anyElementTrue' => '$field'], - 'operator' => 'anyElementTrue', - 'args' => ['$field'], - ], - 'arrayElemAt' => [ - 'expected' => ['$arrayElemAt' => ['$array', '$index']], - 'operator' => 'arrayElemAt', - 'args' => ['$array', '$index'], - ], - 'ceil' => [ + ]; + + yield 'ceil' => [ 'expected' => ['$ceil' => '$field'], 'operator' => 'ceil', 'args' => ['$field'], - ], - 'cmp' => [ - 'expected' => ['$cmp' => ['$field', '$otherField']], - 'operator' => 'cmp', - 'args' => ['$field', '$otherField'], - ], - 'concatWithTwoArgs' => [ - 'expected' => ['$concat' => ['foo', '$field']], - 'operator' => 'concat', - 'args' => ['foo', '$field'], - ], - 'concatWithMultipleArgs' => [ - 'expected' => ['$concat' => ['foo', '$field', '$otherField', 'bleh']], - 'operator' => 'concat', - 'args' => ['foo', '$field', '$otherField', 'bleh'], - ], - 'concatArraysWithTwoArgs' => [ - 'expected' => ['$concatArrays' => [[1, 2, 3], '$array1']], - 'operator' => 'concatArrays', - 'args' => [[1, 2, 3], '$array1'], - ], - 'concatArraysWithMultipleArgs' => [ - 'expected' => ['$concatArrays' => [[1, 2, 3], '$array1', '$array2', [4, 5, 6]]], - 'operator' => 'concatArrays', - 'args' => [[1, 2, 3], '$array1', '$array2', [4, 5, 6]], - ], - 'cond' => [ - 'expected' => ['$cond' => ['if' => ['$gte' => ['$field', 5]], 'then' => '$field', 'else' => '$otherField']], - 'operator' => 'cond', - 'args' => static fn (Expr $expr) => [ - $expr->gte('$field', 5), - '$field', - '$otherField', - ], - ], - 'dateToString' => [ - 'expected' => ['$dateToString' => ['format' => '%Y-%m-%d', 'date' => '$dateField']], - 'operator' => 'dateToString', - 'args' => ['%Y-%m-%d', '$dateField'], - ], - 'dayOfMonth' => [ - 'expected' => ['$dayOfMonth' => '$dateField'], - 'operator' => 'dayOfMonth', - 'args' => ['$dateField'], - ], - 'dayOfWeek' => [ - 'expected' => ['$dayOfWeek' => '$dateField'], - 'operator' => 'dayOfWeek', - 'args' => ['$dateField'], - ], - 'dayOfYear' => [ - 'expected' => ['$dayOfYear' => '$dateField'], - 'operator' => 'dayOfYear', - 'args' => ['$dateField'], - ], - 'divide' => [ + ]; + + yield 'divide' => [ 'expected' => ['$divide' => ['$field', 5]], 'operator' => 'divide', 'args' => ['$field', 5], - ], - 'eq' => [ - 'expected' => ['$eq' => ['$field', '$otherField']], - 'operator' => 'eq', - 'args' => ['$field', '$otherField'], - ], - 'exp' => [ + ]; + + yield 'exp' => [ 'expected' => ['$exp' => '$field'], 'operator' => 'exp', 'args' => ['$field'], - ], - 'filter' => [ - 'expected' => ['$filter' => ['input' => '$array', 'as' => '$as', 'cond' => '$cond']], - 'operator' => 'filter', - 'args' => ['$array', '$as', '$cond'], - ], - 'floor' => [ + ]; + + yield 'floor' => [ 'expected' => ['$floor' => '$field'], 'operator' => 'floor', 'args' => ['$field'], - ], - 'gt' => [ - 'expected' => ['$gt' => ['$field', '$otherField']], - 'operator' => 'gt', - 'args' => ['$field', '$otherField'], - ], - 'gte' => [ - 'expected' => ['$gte' => ['$field', '$otherField']], - 'operator' => 'gte', - 'args' => ['$field', '$otherField'], - ], - 'hour' => [ - 'expected' => ['$hour' => '$dateField'], - 'operator' => 'hour', - 'args' => ['$dateField'], - ], - 'ifNull' => [ - 'expected' => ['$ifNull' => ['$field', '$otherField']], - 'operator' => 'ifNull', - 'args' => ['$field', '$otherField'], - ], - 'in' => [ - 'expected' => ['$in' => ['$field', '$otherField']], - 'operator' => 'in', - 'args' => ['$field', '$otherField'], - ], - 'indexOfArrayWithoutStartOrEnd' => [ - 'expected' => ['$indexOfArray' => ['$field', '$otherField']], - 'operator' => 'indexOfArray', - 'args' => ['$field', '$otherField'], - ], - 'indexOfArrayWithoutStartWithEnd' => [ - 'expected' => ['$indexOfArray' => ['$field', '$otherField']], - 'operator' => 'indexOfArray', - 'args' => ['$field', '$otherField', null, '$end'], - ], - 'indexOfArrayWithStart' => [ - 'expected' => ['$indexOfArray' => ['$field', '$otherField', '$start']], - 'operator' => 'indexOfArray', - 'args' => ['$field', '$otherField', '$start'], - ], - 'indexOfArrayWithStartAndEnd' => [ - 'expected' => ['$indexOfArray' => ['$field', '$otherField', '$start', '$end']], - 'operator' => 'indexOfArray', - 'args' => ['$field', '$otherField', '$start', '$end'], - ], - 'indexOfBytesWithoutStartOrEnd' => [ - 'expected' => ['$indexOfBytes' => ['$field', '$otherField']], - 'operator' => 'indexOfBytes', - 'args' => ['$field', '$otherField'], - ], - 'indexOfBytesWithoutStartWithEnd' => [ - 'expected' => ['$indexOfBytes' => ['$field', '$otherField']], - 'operator' => 'indexOfBytes', - 'args' => ['$field', '$otherField', null, '$end'], - ], - 'indexOfBytesWithStart' => [ - 'expected' => ['$indexOfBytes' => ['$field', '$otherField', '$start']], - 'operator' => 'indexOfBytes', - 'args' => ['$field', '$otherField', '$start'], - ], - 'indexOfBytesWithStartAndEnd' => [ - 'expected' => ['$indexOfBytes' => ['$field', '$otherField', '$start', '$end']], - 'operator' => 'indexOfBytes', - 'args' => ['$field', '$otherField', '$start', '$end'], - ], - 'indexOfCPWithoutStartOrEnd' => [ - 'expected' => ['$indexOfCP' => ['$field', '$otherField']], - 'operator' => 'indexOfCP', - 'args' => ['$field', '$otherField'], - ], - 'indexOfCPWithoutStartWithEnd' => [ - 'expected' => ['$indexOfCP' => ['$field', '$otherField']], - 'operator' => 'indexOfCP', - 'args' => ['$field', '$otherField', null, '$end'], - ], - 'indexOfCPWithStart' => [ - 'expected' => ['$indexOfCP' => ['$field', '$otherField', '$start']], - 'operator' => 'indexOfCP', - 'args' => ['$field', '$otherField', '$start'], - ], - 'indexOfCPWithStartAndEnd' => [ - 'expected' => ['$indexOfCP' => ['$field', '$otherField', '$start', '$end']], - 'operator' => 'indexOfCP', - 'args' => ['$field', '$otherField', '$start', '$end'], - ], - 'isArray' => [ - 'expected' => ['$isArray' => '$field'], - 'operator' => 'isArray', - 'args' => ['$field'], - ], - 'isoDayOfWeek' => [ - 'expected' => ['$isoDayOfWeek' => '$dateField'], - 'operator' => 'isoDayOfWeek', - 'args' => ['$dateField'], - ], - 'isoWeek' => [ - 'expected' => ['$isoWeek' => '$dateField'], - 'operator' => 'isoWeek', - 'args' => ['$dateField'], - ], - 'isoWeekYear' => [ - 'expected' => ['$isoWeekYear' => '$dateField'], - 'operator' => 'isoWeekYear', - 'args' => ['$dateField'], - ], - 'let' => [ - 'expected' => [ - '$let' => [ - 'vars' => [ - 'total' => ['$add' => ['$price', '$tax']], - 'discounted' => ['$cond' => ['if' => '$applyDiscount', 'then' => 0.9, 'else' => 1]], - ], - 'in' => ['$multiply' => ['$$total', '$$discounted']], - ], - ], - 'operator' => 'let', - 'args' => static fn (Expr $expr) => [ - $expr->expr() - ->field('total') - ->add('$price', '$tax') - ->field('discounted') - ->cond('$applyDiscount', 0.9, 1), - $expr->expr() - ->multiply('$$total', '$$discounted'), - ], - ], - 'literal' => [ - 'expected' => ['$literal' => '$field'], - 'operator' => 'literal', - 'args' => ['$field'], - ], - 'ln' => [ + ]; + + yield 'ln' => [ 'expected' => ['$ln' => '$field'], 'operator' => 'ln', 'args' => ['$field'], - ], - 'log' => [ + ]; + + yield 'log' => [ 'expected' => ['$log' => ['$field', '$base']], 'operator' => 'log', 'args' => ['$field', '$base'], - ], - 'log10' => [ + ]; + + yield 'log10' => [ 'expected' => ['$log10' => '$field'], 'operator' => 'log10', 'args' => ['$field'], - ], - 'lt' => [ - 'expected' => ['$lt' => ['$field', '$otherField']], - 'operator' => 'lt', - 'args' => ['$field', '$otherField'], - ], - 'lte' => [ - 'expected' => ['$lte' => ['$field', '$otherField']], - 'operator' => 'lte', - 'args' => ['$field', '$otherField'], - ], - 'map' => [ - 'expected' => ['$map' => ['input' => '$quizzes', 'as' => 'grade', 'in' => ['$add' => ['$$grade', 2]]]], - 'operator' => 'map', - 'args' => static fn (Expr $expr) => [ - '$quizzes', - 'grade', - $expr->add('$$grade', 2), - ], - ], - 'meta' => [ - 'expected' => ['$meta' => '$field'], - 'operator' => 'meta', - 'args' => ['$field'], - ], - 'millisecond' => [ - 'expected' => ['$millisecond' => '$dateField'], - 'operator' => 'millisecond', - 'args' => ['$dateField'], - ], - 'minute' => [ - 'expected' => ['$minute' => '$dateField'], - 'operator' => 'minute', - 'args' => ['$dateField'], - ], - 'mod' => [ + ]; + + yield 'mod' => [ 'expected' => ['$mod' => ['$field', 5]], 'operator' => 'mod', 'args' => ['$field', 5], - ], - 'month' => [ - 'expected' => ['$month' => '$dateField'], - 'operator' => 'month', - 'args' => ['$dateField'], - ], - 'multiply' => [ + ]; + + yield 'multiplyWithTwoArgs' => [ 'expected' => ['$multiply' => ['$field', 5]], 'operator' => 'multiply', 'args' => ['$field', 5], - ], - 'ne' => [ - 'expected' => ['$ne' => ['$field', 5]], - 'operator' => 'ne', + ]; + + yield 'multiplyWithMultipleArgs' => [ + 'expected' => ['$multiply' => ['$field', 5, '$otherField']], + 'operator' => 'multiply', + 'args' => ['$field', 5, '$otherField'], + ]; + + yield 'multiply' => [ + 'expected' => ['$multiply' => ['$field', 5]], + 'operator' => 'multiply', 'args' => ['$field', 5], - ], - 'not' => [ - 'expected' => ['$not' => '$field'], - 'operator' => 'not', - 'args' => ['$field'], - ], - 'pow' => [ + ]; + + yield 'pow' => [ 'expected' => ['$pow' => ['$number', '$exponent']], 'operator' => 'pow', 'args' => ['$number', '$exponent'], + ]; + + yield 'round' => [ + 'expected' => ['$round' => ['$number', '$place']], + 'operator' => 'round', + 'args' => ['$number', '$place'], + ]; + + yield 'sortArray' => [ + 'expected' => ['$sortArray' => ['input' => '$field', 'sortBy' => ['foo' => 1]]], + 'operator' => 'sortArray', + 'args' => ['$field', ['foo' => 1]], + ]; + + yield 'sqrt' => [ + 'expected' => ['$sqrt' => '$field'], + 'operator' => 'sqrt', + 'args' => ['$field'], + ]; + + yield 'subtract' => [ + 'expected' => ['$subtract' => ['$field', '$otherField']], + 'operator' => 'subtract', + 'args' => ['$field', '$otherField'], + ]; + + yield 'trunc' => [ + 'expected' => ['$trunc' => '$field'], + 'operator' => 'trunc', + 'args' => ['$field'], + ]; + } + + public static function provideArrayExpressionOperators(): Generator + { + yield 'arrayElemAt' => [ + 'expected' => ['$arrayElemAt' => ['$array', '$index']], + 'operator' => 'arrayElemAt', + 'args' => ['$array', '$index'], + ]; + + yield 'arrayToObject' => [ + 'expected' => ['$arrayToObject' => [['item', 'abc123'], ['qty', 25]]], + 'operator' => 'arrayToObject', + 'args' => [[['item', 'abc123'], ['qty', 25]]], + ]; + + yield 'concatArraysWithTwoArgs' => [ + 'expected' => ['$concatArrays' => [[1, 2, 3], '$array1']], + 'operator' => 'concatArrays', + 'args' => [[1, 2, 3], '$array1'], + ]; + + yield 'concatArraysWithMultipleArgs' => [ + 'expected' => ['$concatArrays' => [[1, 2, 3], '$array1', '$array2', [4, 5, 6]]], + 'operator' => 'concatArrays', + 'args' => [[1, 2, 3], '$array1', '$array2', [4, 5, 6]], + ]; + + yield 'filter' => [ + 'expected' => ['$filter' => ['input' => '$array', 'as' => '$as', 'cond' => '$cond']], + 'operator' => 'filter', + 'args' => ['$array', '$as', '$cond'], + ]; + + yield 'first' => [ + 'expected' => ['$first' => '$array'], + 'operator' => 'first', + 'args' => ['$array'], + ]; + + yield 'firstN' => [ + 'expected' => ['$firstN' => ['input' => '$array', 'n' => '$n']], + 'operator' => 'firstN', + 'args' => ['$array', '$n'], + ]; + + yield 'in' => [ + 'expected' => ['$in' => ['$field', '$otherField']], + 'operator' => 'in', + 'args' => ['$field', '$otherField'], + ]; + + yield 'indexOfArrayWithoutStartOrEnd' => [ + 'expected' => ['$indexOfArray' => ['$field', '$otherField']], + 'operator' => 'indexOfArray', + 'args' => ['$field', '$otherField'], + ]; + + yield 'indexOfArrayWithoutStartWithEnd' => [ + 'expected' => ['$indexOfArray' => ['$field', '$otherField']], + 'operator' => 'indexOfArray', + 'args' => ['$field', '$otherField', null, '$end'], + ]; + + yield 'indexOfArrayWithStart' => [ + 'expected' => ['$indexOfArray' => ['$field', '$otherField', '$start']], + 'operator' => 'indexOfArray', + 'args' => ['$field', '$otherField', '$start'], + ]; + + yield 'indexOfArrayWithStartAndEnd' => [ + 'expected' => ['$indexOfArray' => ['$field', '$otherField', '$start', '$end']], + 'operator' => 'indexOfArray', + 'args' => ['$field', '$otherField', '$start', '$end'], + ]; + + yield 'isArray' => [ + 'expected' => ['$isArray' => '$field'], + 'operator' => 'isArray', + 'args' => ['$field'], + ]; + + yield 'last' => [ + 'expected' => ['$last' => '$array'], + 'operator' => 'last', + 'args' => ['$array'], + ]; + + yield 'lastN' => [ + 'expected' => ['$lastN' => ['input' => '$array', 'n' => '$n']], + 'operator' => 'lastN', + 'args' => ['$array', '$n'], + ]; + + yield 'map' => [ + 'expected' => ['$map' => ['input' => '$quizzes', 'as' => 'grade', 'in' => ['$add' => ['$$grade', 2]]]], + 'operator' => 'map', + 'args' => static fn (Expr $expr) => [ + '$quizzes', + 'grade', + $expr->add('$$grade', 2), ], - 'rangeWithoutStep' => [ - 'expected' => ['$range' => ['$start', '$end', 1]], - 'operator' => 'range', - 'args' => ['$start', '$end'], - ], - 'rangeWithStep' => [ - 'expected' => ['$range' => ['$start', '$end', 5]], - 'operator' => 'range', - 'args' => ['$start', '$end', 5], - ], - 'reduce' => [ - 'expected' => [ - '$reduce' => [ - 'input' => '$array', - 'initialValue' => ['sum' => 0, 'product' => 1], - 'in' => [ - '$add' => ['$$value.sum', '$$this'], - '$multiply' => ['$$value.product', '$$this'], - ], + ]; + + yield 'maxN' => [ + 'expected' => ['$maxN' => ['input' => '$array', 'n' => '$n']], + 'operator' => 'maxN', + 'args' => ['$array', '$n'], + ]; + + yield 'minN' => [ + 'expected' => ['$minN' => ['input' => '$array', 'n' => '$n']], + 'operator' => 'minN', + 'args' => ['$array', '$n'], + ]; + + yield 'objectToArray' => [ + 'expected' => ['$objectToArray' => ['$obj']], + 'operator' => 'objectToArray', + 'args' => [['$obj']], + ]; + + yield 'rangeWithoutStep' => [ + 'expected' => ['$range' => ['$start', '$end']], + 'operator' => 'range', + 'args' => ['$start', '$end'], + ]; + + yield 'rangeWithStep' => [ + 'expected' => ['$range' => ['$start', '$end', 5]], + 'operator' => 'range', + 'args' => ['$start', '$end', 5], + ]; + + yield 'reduce' => [ + 'expected' => [ + '$reduce' => [ + 'input' => '$array', + 'initialValue' => ['sum' => 0, 'product' => 1], + 'in' => [ + '$add' => ['$$value.sum', '$$this'], + '$multiply' => ['$$value.product', '$$this'], ], ], - 'operator' => 'reduce', - 'args' => static fn (Expr $expr) => [ - '$array', - ['sum' => 0, 'product' => 1], - $expr + ], + 'operator' => 'reduce', + 'args' => static fn (Expr $expr) => [ + '$array', + ['sum' => 0, 'product' => 1], + $expr ->add('$$value.sum', '$$this') ->multiply('$$value.product', '$$this'), - ], - ], - 'reverseArray' => [ - 'expected' => ['$reverseArray' => '$array'], - 'operator' => 'reverseArray', - 'args' => ['$array'], ], - 'second' => [ - 'expected' => ['$second' => '$dateField'], - 'operator' => 'second', - 'args' => ['$dateField'], - ], - 'setDifference' => [ - 'expected' => ['$setDifference' => ['$field', '$otherField']], - 'operator' => 'setDifference', - 'args' => ['$field', '$otherField'], - ], - 'setEqualsWithTwoSets' => [ - 'expected' => ['$setEquals' => ['$set1', '$set2']], - 'operator' => 'setEquals', - 'args' => ['$set1', '$set2'], + ]; + + yield 'reverseArray' => [ + 'expected' => ['$reverseArray' => '$array'], + 'operator' => 'reverseArray', + 'args' => ['$array'], + ]; + + yield 'size' => [ + 'expected' => ['$size' => '$field'], + 'operator' => 'size', + 'args' => ['$field'], + ]; + + yield 'sliceWithoutPosition' => [ + 'expected' => ['$slice' => ['$array', '$n']], + 'operator' => 'slice', + 'args' => ['$array', '$n'], + ]; + + yield 'sliceWithPosition' => [ + 'expected' => ['$slice' => ['$array', '$position', '$n']], + 'operator' => 'slice', + 'args' => ['$array', '$n', '$position'], + ]; + + yield 'zipWithoutExtraFields' => [ + 'expected' => ['$zip' => ['inputs' => ['$array1', '$array2']]], + 'operator' => 'zip', + 'args' => [['$array1', '$array2']], + ]; + + yield 'zipWithUseLongestLengthWithoutDefault' => [ + 'expected' => ['$zip' => ['inputs' => ['$array1', '$array2'], 'useLongestLength' => true]], + 'operator' => 'zip', + 'args' => [['$array1', '$array2'], true], + ]; + + yield 'zipWithUseLongestLengthAndDefault' => [ + 'expected' => ['$zip' => ['inputs' => ['$array1', '$array2'], 'useLongestLength' => true, 'defaults' => ['a', 'b']]], + 'operator' => 'zip', + 'args' => [['$array1', '$array2'], true, ['a', 'b']], + ]; + } + + public static function provideBooleanExpressionOperators(): Generator + { + yield 'and' => [ + 'expected' => ['$and' => ['$field', '$otherField']], + 'operator' => 'and', + 'args' => ['$field', '$otherField'], + ]; + + yield 'or' => [ + 'expected' => ['$or' => ['$field', '$otherField']], + 'operator' => 'or', + 'args' => ['$field', '$otherField'], + ]; + + yield 'not' => [ + 'expected' => ['$not' => '$field'], + 'operator' => 'not', + 'args' => ['$field'], + ]; + } + + public static function provideComparisonExpressionOperators(): Generator + { + yield 'cmp' => [ + 'expected' => ['$cmp' => ['$field', '$otherField']], + 'operator' => 'cmp', + 'args' => ['$field', '$otherField'], + ]; + + yield 'eq' => [ + 'expected' => ['$eq' => ['$field', '$otherField']], + 'operator' => 'eq', + 'args' => ['$field', '$otherField'], + ]; + + yield 'gt' => [ + 'expected' => ['$gt' => ['$field', '$otherField']], + 'operator' => 'gt', + 'args' => ['$field', '$otherField'], + ]; + + yield 'gte' => [ + 'expected' => ['$gte' => ['$field', '$otherField']], + 'operator' => 'gte', + 'args' => ['$field', '$otherField'], + ]; + + yield 'lt' => [ + 'expected' => ['$lt' => ['$field', '$otherField']], + 'operator' => 'lt', + 'args' => ['$field', '$otherField'], + ]; + + yield 'lte' => [ + 'expected' => ['$lte' => ['$field', '$otherField']], + 'operator' => 'lte', + 'args' => ['$field', '$otherField'], + ]; + + yield 'ne' => [ + 'expected' => ['$ne' => ['$field', 5]], + 'operator' => 'ne', + 'args' => ['$field', 5], + ]; + } + + public static function provideConditionalExpressionOperators(): Generator + { + yield 'cond' => [ + 'expected' => ['$cond' => ['if' => ['$gte' => ['$field', 5]], 'then' => '$field', 'else' => '$otherField']], + 'operator' => 'cond', + 'args' => static fn (Expr $expr) => [ + $expr->gte('$field', 5), + '$field', + '$otherField', ], - 'setEqualsWithMultipleSets' => [ - 'expected' => ['$setEquals' => ['$set1', '$set2', '$set3', '$set4']], - 'operator' => 'setEquals', - 'args' => ['$set1', '$set2', '$set3', '$set4'], + ]; + + yield 'ifNull' => [ + 'expected' => ['$ifNull' => ['$field', '$otherField']], + 'operator' => 'ifNull', + 'args' => ['$field', '$otherField'], + ]; + } + + public static function provideCustomExpressionOperators(): Generator + { + yield 'accumulatorWithRequiredArgs' => [ + 'expected' => [ + '$accumulator' => [ + 'init' => 'function() { return 0; }', + 'accumulate' => 'function(state, value) { return state + value; }', + 'accumulateArgs' => ['$value'], + 'merge' => 'function(state1, state2) { return state1 + state2; }', + 'lang' => 'js', + ], ], - 'setIntersectionWithTwoSets' => [ - 'expected' => ['$setIntersection' => ['$set1', '$set2']], - 'operator' => 'setIntersection', - 'args' => ['$set1', '$set2'], + 'operator' => 'accumulator', + 'args' => [ + 'function() { return 0; }', + 'function(state, value) { return state + value; }', + ['$value'], + 'function(state1, state2) { return state1 + state2; }', ], - 'setIntersectionWithMultipleSets' => [ - 'expected' => ['$setIntersection' => ['$set1', '$set2', '$set3', '$set4']], - 'operator' => 'setIntersection', - 'args' => ['$set1', '$set2', '$set3', '$set4'], + ]; + + yield 'accumulatorWithAllArgs' => [ + 'expected' => [ + '$accumulator' => [ + 'init' => 'function(initial) { return initial; }', + 'initArgs' => [1], + 'accumulate' => 'function(state, value) { return state + value; }', + 'accumulateArgs' => ['$value'], + 'merge' => 'function(state1, state2) { return state1 + state2; }', + 'finalize' => 'function(state) { return state; }', + 'lang' => 'js', + ], ], - 'setIsSubset' => [ - 'expected' => ['$setIsSubset' => ['$field', '$otherField']], - 'operator' => 'setIsSubset', - 'args' => ['$field', '$otherField'], + 'operator' => 'accumulator', + 'args' => [ + 'function(initial) { return initial; }', + 'function(state, value) { return state + value; }', + ['$value'], + 'function(state1, state2) { return state1 + state2; }', + [1], + 'function(state) { return state; }', ], - 'setUnionWithTwoSets' => [ - 'expected' => ['$setUnion' => ['$set1', '$set2']], - 'operator' => 'setUnion', - 'args' => ['$set1', '$set2'], + ]; + + yield 'function' => [ + 'expected' => [ + '$function' => [ + 'body' => 'function(value) { return value; }', + 'args' => ['$field'], + 'lang' => 'js', + ], ], - 'setUnionWithMultipleSets' => [ - 'expected' => ['$setUnion' => ['$set1', '$set2', '$set3', '$set4']], - 'operator' => 'setUnion', - 'args' => ['$set1', '$set2', '$set3', '$set4'], + 'operator' => 'function', + 'args' => [ + 'function(value) { return value; }', + ['$field'], ], - 'size' => [ - 'expected' => ['$size' => '$field'], - 'operator' => 'size', - 'args' => ['$field'], - ], - 'sliceWithoutPosition' => [ - 'expected' => ['$slice' => ['$array', '$n']], - 'operator' => 'slice', - 'args' => ['$array', '$n'], - ], - 'sliceWithPosition' => [ - 'expected' => ['$slice' => ['$array', '$position', '$n']], - 'operator' => 'slice', - 'args' => ['$array', '$n', '$position'], - ], - 'split' => [ - 'expected' => ['$split' => ['$string', '$delimiter']], - 'operator' => 'split', - 'args' => ['$string', '$delimiter'], - ], - 'sqrt' => [ - 'expected' => ['$sqrt' => '$field'], - 'operator' => 'sqrt', - 'args' => ['$field'], - ], - 'strcasecmp' => [ - 'expected' => ['$strcasecmp' => ['$field', '$otherField']], - 'operator' => 'strcasecmp', - 'args' => ['$field', '$otherField'], - ], - 'strLenBytes' => [ - 'expected' => ['$strLenBytes' => '$field'], - 'operator' => 'strLenBytes', - 'args' => ['$field'], - ], - 'strLenCP' => [ - 'expected' => ['$strLenCP' => '$field'], - 'operator' => 'strLenCP', - 'args' => ['$field'], - ], - 'substr' => [ - 'expected' => ['$substr' => ['$field', 0, '$length']], - 'operator' => 'substr', - 'args' => ['$field', 0, '$length'], - ], - 'substrBytes' => [ - 'expected' => ['$substrBytes' => ['$field', 0, '$length']], - 'operator' => 'substrBytes', - 'args' => ['$field', 0, '$length'], - ], - 'substrCP' => [ - 'expected' => ['$substrCP' => ['$field', 0, '$length']], - 'operator' => 'substrCP', - 'args' => ['$field', 0, '$length'], - ], - 'subtract' => [ - 'expected' => ['$subtract' => ['$field', '$otherField']], - 'operator' => 'subtract', - 'args' => ['$field', '$otherField'], - ], - 'toLower' => [ - 'expected' => ['$toLower' => '$field'], - 'operator' => 'toLower', - 'args' => ['$field'], - ], - 'toUpper' => [ - 'expected' => ['$toUpper' => '$field'], - 'operator' => 'toUpper', - 'args' => ['$field'], - ], - 'trunc' => [ - 'expected' => ['$trunc' => '$field'], - 'operator' => 'trunc', - 'args' => ['$field'], - ], - 'type' => [ - 'expected' => ['$type' => '$field'], - 'operator' => 'type', - 'args' => ['$field'], - ], - 'week' => [ - 'expected' => ['$week' => '$dateField'], - 'operator' => 'week', - 'args' => ['$dateField'], - ], - 'year' => [ - 'expected' => ['$year' => '$dateField'], - 'operator' => 'year', - 'args' => ['$dateField'], - ], - 'zipWithoutExtraFields' => [ - 'expected' => ['$zip' => ['inputs' => ['$array1', '$array2']]], - 'operator' => 'zip', - 'args' => [['$array1', '$array2']], - ], - 'zipWithUseLongestLengthWithoutDefault' => [ - 'expected' => ['$zip' => ['inputs' => ['$array1', '$array2'], 'useLongestLength' => true]], - 'operator' => 'zip', - 'args' => [['$array1', '$array2'], true], - ], - 'zipWithUseLongestLengthAndDefault' => [ - 'expected' => ['$zip' => ['inputs' => ['$array1', '$array2'], 'useLongestLength' => true, 'defaults' => ['a', 'b']]], - 'operator' => 'zip', - 'args' => [['$array1', '$array2'], true, ['a', 'b']], - ], - 'arrayToObject' => [ - 'expected' => ['$arrayToObject' => ['$array']], - 'operator' => 'arrayToObject', - 'args' => [['$array']], - ], - 'objectToArray' => [ - 'expected' => ['$objectToArray' => ['$obj']], - 'operator' => 'objectToArray', - 'args' => [['$obj']], - ], - 'round' => [ - 'expected' => ['$round' => ['$number', '$place']], - 'operator' => 'round', - 'args' => ['$number', '$place'], - ], - 'ltrim' => [ - 'expected' => ['$ltrim' => ['$input', '$chars']], - 'operator' => 'ltrim', - 'args' => ['$input', '$chars'], - ], - 'rtrim' => [ - 'expected' => ['$rtrim' => ['$input', '$chars']], - 'operator' => 'rtrim', - 'args' => ['$input', '$chars'], - ], - 'trim' => [ - 'expected' => ['$trim' => ['$input', '$chars']], - 'operator' => 'trim', - 'args' => ['$input', '$chars'], - ], - 'sin' => [ - 'expected' => ['$sin' => '$field'], - 'operator' => 'sin', - 'args' => ['$field'], - ], - 'cos' => [ - 'expected' => ['$cos' => '$field'], - 'operator' => 'cos', - 'args' => ['$field'], - ], - 'tan' => [ - 'expected' => ['$tan' => '$field'], - 'operator' => 'tan', - 'args' => ['$field'], - ], - 'asin' => [ - 'expected' => ['$asin' => '$field'], - 'operator' => 'asin', - 'args' => ['$field'], - ], - 'acos' => [ - 'expected' => ['$acos' => '$field'], - 'operator' => 'acos', - 'args' => ['$field'], - ], - 'atan' => [ - 'expected' => ['$atan' => '$field'], - 'operator' => 'atan', - 'args' => ['$field'], - ], - 'atan2' => [ - 'expected' => ['$atan2' => ['$expr1', '$expr2']], - 'operator' => 'atan2', - 'args' => ['$expr1', '$expr2'], - ], - 'sinh' => [ - 'expected' => ['$sinh' => '$field'], - 'operator' => 'sinh', - 'args' => ['$field'], - ], - 'cosh' => [ - 'expected' => ['$cosh' => '$field'], - 'operator' => 'cosh', - 'args' => ['$field'], - ], - 'tanh' => [ - 'expected' => ['$tanh' => '$field'], - 'operator' => 'tanh', - 'args' => ['$field'], - ], - 'degreesToRadians' => [ - 'expected' => ['$degreesToRadians' => '$field'], - 'operator' => 'degreesToRadians', - 'args' => ['$field'], - ], - 'radiansToDegrees' => [ - 'expected' => ['$radiansToDegrees' => '$field'], - 'operator' => 'radiansToDegrees', - 'args' => ['$field'], - ], - 'convert' => [ - 'expected' => ['$convert' => ['input' => '$field', 'to' => '$to']], - 'operator' => 'convert', - 'args' => ['$field', '$to', null, null], - ], - 'isNumber' => [ - 'expected' => ['$isNumber' => '$field'], - 'operator' => 'isNumber', - 'args' => ['$field'], - ], - 'toBool' => [ - 'expected' => ['$toBool' => '$field'], - 'operator' => 'toBool', - 'args' => ['$field'], - ], - 'toDate' => [ - 'expected' => ['$toDate' => '$field'], - 'operator' => 'toDate', - 'args' => ['$field'], - ], - 'toDecimal' => [ - 'expected' => ['$toDecimal' => '$field'], - 'operator' => 'toDecimal', - 'args' => ['$field'], - ], - 'toDouble' => [ - 'expected' => ['$toDouble' => '$field'], - 'operator' => 'toDouble', - 'args' => ['$field'], - ], - 'toInt' => [ - 'expected' => ['$toInt' => '$field'], - 'operator' => 'toInt', - 'args' => ['$field'], - ], - 'toLong' => [ - 'expected' => ['$toLong' => '$field'], - 'operator' => 'toLong', - 'args' => ['$field'], - ], - 'toObjectId' => [ - 'expected' => ['$toObjectId' => '$field'], - 'operator' => 'toObjectId', - 'args' => ['$field'], + ]; + } + + public static function provideDataSizeExpressionOperators(): Generator + { + yield 'binarySize' => [ + 'expected' => ['$binarySize' => '$field'], + 'operator' => 'binarySize', + 'args' => ['$field'], + ]; + + yield 'bsonSize' => [ + 'expected' => ['$bsonSize' => '$field'], + 'operator' => 'bsonSize', + 'args' => ['$field'], + ]; + } + + public static function provideDateExpressionOperators(): Generator + { + yield 'dateAdd' => [ + 'expected' => ['$dateAdd' => ['startDate' => '$dateField', 'unit' => 'day', 'amount' => 1]], + 'operator' => 'dateAdd', + 'args' => ['$dateField', 'day', 1], + ]; + + yield 'dateAddWithTimezone' => [ + 'expected' => ['$dateAdd' => ['startDate' => '$dateField', 'unit' => 'day', 'amount' => 1, 'timezone' => '$timezone']], + 'operator' => 'dateAdd', + 'args' => ['$dateField', 'day', 1, '$timezone'], + ]; + + yield 'dateDiff' => [ + 'expected' => ['$dateDiff' => ['startDate' => '$dateField', 'endDate' => '$otherDateField', 'unit' => 'day']], + 'operator' => 'dateDiff', + 'args' => ['$dateField', '$otherDateField', 'day'], + ]; + + yield 'dateDiffWithTimezone' => [ + 'expected' => ['$dateDiff' => ['startDate' => '$dateField', 'endDate' => '$otherDateField', 'unit' => 'day', 'timezone' => '$timezone']], + 'operator' => 'dateDiff', + 'args' => ['$dateField', '$otherDateField', 'day', '$timezone'], + ]; + + yield 'dateDiffWithTimezoneAndStartOfWeek' => [ + 'expected' => ['$dateDiff' => ['startDate' => '$dateField', 'endDate' => '$otherDateField', 'unit' => 'day', 'timezone' => '$timezone', 'startOfWeek' => '$startOfWeek']], + 'operator' => 'dateDiff', + 'args' => ['$dateField', '$otherDateField', 'day', '$timezone', '$startOfWeek'], + ]; + + yield 'dateFromPartsYearOnly' => [ + 'expected' => ['$dateFromParts' => ['year' => 2019]], + 'operator' => 'dateFromParts', + 'args' => [2019], + ]; + + yield 'dateFromPartsWithMonth' => [ + 'expected' => ['$dateFromParts' => ['year' => 2019, 'month' => 3]], + 'operator' => 'dateFromParts', + 'args' => [2019, null, 3], + ]; + + yield 'dateFromPartsWithDay' => [ + 'expected' => ['$dateFromParts' => ['year' => 2019, 'month' => 3, 'day' => 14]], + 'operator' => 'dateFromParts', + 'args' => [2019, null, 3, null, 14], + ]; + + yield 'dateFromPartsWithHour' => [ + 'expected' => ['$dateFromParts' => ['year' => 2019, 'month' => 3, 'day' => 14, 'hour' => 10]], + 'operator' => 'dateFromParts', + 'args' => [2019, null, 3, null, 14, null, 10], + ]; + + yield 'dateFromPartsWithMinute' => [ + 'expected' => ['$dateFromParts' => ['year' => 2019, 'month' => 3, 'day' => 14, 'hour' => 10, 'minute' => 13]], + 'operator' => 'dateFromParts', + 'args' => [2019, null, 3, null, 14, null, 10, 13], + ]; + + yield 'dateFromPartsWithSecond' => [ + 'expected' => ['$dateFromParts' => ['year' => 2019, 'month' => 3, 'day' => 14, 'hour' => 10, 'minute' => 13, 'second' => 27]], + 'operator' => 'dateFromParts', + 'args' => [2019, null, 3, null, 14, null, 10, 13, 27], + ]; + + yield 'dateFromPartsWithMillisecond' => [ + 'expected' => ['$dateFromParts' => ['year' => 2019, 'month' => 3, 'day' => 14, 'hour' => 10, 'minute' => 13, 'second' => 27, 'millisecond' => 123]], + 'operator' => 'dateFromParts', + 'args' => [2019, null, 3, null, 14, null, 10, 13, 27, 123], + ]; + + yield 'dateFromPartsWithTimezone' => [ + 'expected' => ['$dateFromParts' => ['year' => 2019, 'month' => 3, 'day' => 14, 'hour' => 10, 'minute' => 13, 'second' => 27, 'millisecond' => 123, 'timezone' => '$timezone']], + 'operator' => 'dateFromParts', + 'args' => [2019, null, 3, null, 14, null, 10, 13, 27, 123, '$timezone'], + ]; + + yield 'dateFromPartsIsoWeekYearOnly' => [ + 'expected' => ['$dateFromParts' => ['isoWeekYear' => 2020]], + 'operator' => 'dateFromParts', + 'args' => [null, 2020], + ]; + + yield 'dateFromPartsWithIsoWeek' => [ + 'expected' => ['$dateFromParts' => ['isoWeekYear' => 2020, 'isoWeek' => 5]], + 'operator' => 'dateFromParts', + 'args' => [null, 2020, null, 5], + ]; + + yield 'dateFromPartsWithIsoDayOfWeek' => [ + 'expected' => ['$dateFromParts' => ['isoWeekYear' => 2020, 'isoWeek' => 5, 'isoDayOfWeek' => 7]], + 'operator' => 'dateFromParts', + 'args' => [null, 2020, null, 5, null, 7], + ]; + + yield 'dateFromString' => [ + 'expected' => ['$dateFromString' => ['dateString' => '2019-14-03']], + 'operator' => 'dateFromString', + 'args' => ['2019-14-03'], + ]; + + yield 'dateFromStringWithFormat' => [ + 'expected' => ['$dateFromString' => ['dateString' => '2019-14-03', 'format' => '%Y-%m-%d']], + 'operator' => 'dateFromString', + 'args' => ['2019-14-03', '%Y-%m-%d'], + ]; + + yield 'dateFromStringWithTimezone' => [ + 'expected' => ['$dateFromString' => ['dateString' => '2019-14-03', 'format' => '%Y-%m-%d', 'timezone' => '$timezone']], + 'operator' => 'dateFromString', + 'args' => ['2019-14-03', '%Y-%m-%d', '$timezone'], + ]; + + yield 'dateFromStringWithOnError' => [ + 'expected' => ['$dateFromString' => ['dateString' => '2019-14-03', 'format' => '%Y-%m-%d', 'timezone' => '$timezone', 'onError' => '$defaultDate']], + 'operator' => 'dateFromString', + 'args' => ['2019-14-03', '%Y-%m-%d', '$timezone', '$defaultDate'], + ]; + + yield 'dateFromStringWithOnNull' => [ + 'expected' => ['$dateFromString' => ['dateString' => '2019-14-03', 'format' => '%Y-%m-%d', 'timezone' => '$timezone', 'onError' => '$defaultDate', 'onNull' => '$defaultDate']], + 'operator' => 'dateFromString', + 'args' => ['2019-14-03', '%Y-%m-%d', '$timezone', '$defaultDate', '$defaultDate'], + ]; + + yield 'dateSubtract' => [ + 'expected' => ['$dateSubtract' => ['startDate' => '$dateField', 'unit' => 'day', 'amount' => 1]], + 'operator' => 'dateSubtract', + 'args' => ['$dateField', 'day', 1], + ]; + + yield 'dateSubtractWithTimezone' => [ + 'expected' => ['$dateSubtract' => ['startDate' => '$dateField', 'unit' => 'day', 'amount' => 1, 'timezone' => '$timezone']], + 'operator' => 'dateSubtract', + 'args' => ['$dateField', 'day', 1, '$timezone'], + ]; + + yield 'dateToParts' => [ + 'expected' => ['$dateToParts' => ['date' => '$dateField']], + 'operator' => 'dateToParts', + 'args' => ['$dateField'], + ]; + + yield 'dateToPartsWithTimezone' => [ + 'expected' => ['$dateToParts' => ['date' => '$dateField', 'timezone' => '$timezone']], + 'operator' => 'dateToParts', + 'args' => ['$dateField', '$timezone'], + ]; + + yield 'dateToPartsWithIso8601' => [ + 'expected' => ['$dateToParts' => ['date' => '$dateField', 'timezone' => '$timezone', 'iso8601' => true]], + 'operator' => 'dateToParts', + 'args' => ['$dateField', '$timezone', true], + ]; + + yield 'dateToString' => [ + 'expected' => ['$dateToString' => ['date' => '$dateField', 'format' => '%Y-%m-%d']], + 'operator' => 'dateToString', + 'args' => ['%Y-%m-%d', '$dateField'], + ]; + + yield 'dateToStringWithTimezone' => [ + 'expected' => ['$dateToString' => ['date' => '$dateField', 'format' => '%Y-%m-%d', 'timezone' => '$timezone']], + 'operator' => 'dateToString', + 'args' => ['%Y-%m-%d', '$dateField', '$timezone'], + ]; + + yield 'dateToStringWithOnNull' => [ + 'expected' => ['$dateToString' => ['date' => '$dateField', 'format' => '%Y-%m-%d', 'timezone' => '$timezone', 'onNull' => '$defaultDate']], + 'operator' => 'dateToString', + 'args' => ['%Y-%m-%d', '$dateField', '$timezone', '$defaultDate'], + ]; + + yield 'dateTrunc' => [ + 'expected' => ['$dateTrunc' => ['date' => '$dateField', 'unit' => 'day']], + 'operator' => 'dateTrunc', + 'args' => ['$dateField', 'day'], + ]; + + yield 'dateTruncWithBinSize' => [ + 'expected' => ['$dateTrunc' => ['date' => '$dateField', 'unit' => 'day', 'binSize' => 2]], + 'operator' => 'dateTrunc', + 'args' => ['$dateField', 'day', 2], + ]; + + yield 'dateTruncWithTimezone' => [ + 'expected' => ['$dateTrunc' => ['date' => '$dateField', 'unit' => 'day', 'binSize' => 2, 'timezone' => '$timezone']], + 'operator' => 'dateTrunc', + 'args' => ['$dateField', 'day', 2, '$timezone'], + ]; + + yield 'dateTruncWithStartOfWeek' => [ + 'expected' => ['$dateTrunc' => ['date' => '$dateField', 'unit' => 'day', 'binSize' => 2, 'timezone' => '$timezone', 'startOfWeek' => 'monday']], + 'operator' => 'dateTrunc', + 'args' => ['$dateField', 'day', 2, '$timezone', 'monday'], + ]; + + yield 'dayOfMonth' => [ + 'expected' => ['$dayOfMonth' => '$dateField'], + 'operator' => 'dayOfMonth', + 'args' => ['$dateField'], + ]; + + yield 'dayOfWeek' => [ + 'expected' => ['$dayOfWeek' => '$dateField'], + 'operator' => 'dayOfWeek', + 'args' => ['$dateField'], + ]; + + yield 'dayOfYear' => [ + 'expected' => ['$dayOfYear' => '$dateField'], + 'operator' => 'dayOfYear', + 'args' => ['$dateField'], + ]; + + yield 'hour' => [ + 'expected' => ['$hour' => '$dateField'], + 'operator' => 'hour', + 'args' => ['$dateField'], + ]; + + yield 'isoDayOfWeek' => [ + 'expected' => ['$isoDayOfWeek' => '$dateField'], + 'operator' => 'isoDayOfWeek', + 'args' => ['$dateField'], + ]; + + yield 'isoWeek' => [ + 'expected' => ['$isoWeek' => '$dateField'], + 'operator' => 'isoWeek', + 'args' => ['$dateField'], + ]; + + yield 'isoWeekYear' => [ + 'expected' => ['$isoWeekYear' => '$dateField'], + 'operator' => 'isoWeekYear', + 'args' => ['$dateField'], + ]; + + yield 'millisecond' => [ + 'expected' => ['$millisecond' => '$dateField'], + 'operator' => 'millisecond', + 'args' => ['$dateField'], + ]; + + yield 'minute' => [ + 'expected' => ['$minute' => '$dateField'], + 'operator' => 'minute', + 'args' => ['$dateField'], + ]; + + yield 'month' => [ + 'expected' => ['$month' => '$dateField'], + 'operator' => 'month', + 'args' => ['$dateField'], + ]; + + yield 'second' => [ + 'expected' => ['$second' => '$dateField'], + 'operator' => 'second', + 'args' => ['$dateField'], + ]; + + yield 'week' => [ + 'expected' => ['$week' => '$dateField'], + 'operator' => 'week', + 'args' => ['$dateField'], + ]; + + yield 'year' => [ + 'expected' => ['$year' => '$dateField'], + 'operator' => 'year', + 'args' => ['$dateField'], + ]; + } + + public static function provideGroupAccumulatorExpressionOperators(): Generator + { + yield 'addToSet (group)' => [ + 'expected' => ['$addToSet' => '$field'], + 'operator' => 'addToSet', + 'args' => ['$field'], + ]; + + yield 'avg (group)' => [ + 'expected' => ['$avg' => '$field'], + 'operator' => 'avg', + 'args' => ['$field'], + ]; + + yield 'bottom (group)' => [ + 'expected' => ['$bottom' => ['output' => '$field', 'sortBy' => ['foo' => 1]]], + 'operator' => 'bottom', + 'args' => ['$field', ['foo' => 1]], + ]; + + yield 'bottomN (group)' => [ + 'expected' => ['$bottomN' => ['output' => '$field', 'sortBy' => ['foo' => 1], 'n' => 5]], + 'operator' => 'bottomN', + 'args' => ['$field', ['foo' => 1], 5], + ]; + + yield 'count (group)' => [ + 'expected' => ['$count' => []], + 'operator' => 'countDocuments', + 'args' => [], + ]; + + yield 'first (group)' => [ + 'expected' => ['$first' => '$field'], + 'operator' => 'first', + 'args' => ['$field'], + ]; + + yield 'firstN (group)' => [ + 'expected' => ['$firstN' => ['input' => '$field', 'n' => 5]], + 'operator' => 'firstN', + 'args' => ['$field', 5], + ]; + + yield 'last (group)' => [ + 'expected' => ['$last' => '$field'], + 'operator' => 'last', + 'args' => ['$field'], + ]; + + yield 'lastN (group)' => [ + 'expected' => ['$lastN' => ['input' => '$field', 'n' => 5]], + 'operator' => 'lastN', + 'args' => ['$field', 5], + ]; + + yield 'max (group)' => [ + 'expected' => ['$max' => '$field'], + 'operator' => 'max', + 'args' => ['$field'], + ]; + + yield 'maxN (group)' => [ + 'expected' => ['$maxN' => ['input' => '$field', 'n' => 5]], + 'operator' => 'maxN', + 'args' => ['$field', 5], + ]; + + yield 'min (group)' => [ + 'expected' => ['$min' => '$field'], + 'operator' => 'min', + 'args' => ['$field'], + ]; + + yield 'minN (group)' => [ + 'expected' => ['$minN' => ['input' => '$field', 'n' => 5]], + 'operator' => 'minN', + 'args' => ['$field', 5], + ]; + + yield 'push (group)' => [ + 'expected' => ['$push' => '$field'], + 'operator' => 'push', + 'args' => ['$field'], + ]; + + yield 'stdDevPop (group)' => [ + 'expected' => ['$stdDevPop' => '$field'], + 'operator' => 'stdDevPop', + 'args' => ['$field'], + ]; + + yield 'stdDevSamp (group)' => [ + 'expected' => ['$stdDevSamp' => '$field'], + 'operator' => 'stdDevSamp', + 'args' => ['$field'], + ]; + + yield 'sum (group)' => [ + 'expected' => ['$sum' => '$field'], + 'operator' => 'sum', + 'args' => ['$field'], + ]; + + yield 'top (group)' => [ + 'expected' => ['$top' => ['output' => '$field', 'sortBy' => ['foo' => 1]]], + 'operator' => 'top', + 'args' => ['$field', ['foo' => 1]], + ]; + + yield 'topN (group)' => [ + 'expected' => ['$topN' => ['output' => '$field', 'sortBy' => ['foo' => 1], 'n' => 5]], + 'operator' => 'topN', + 'args' => ['$field', ['foo' => 1], 5], + ]; + } + + public static function provideMiscExpressionOperators(): Generator + { + yield 'let' => [ + 'expected' => [ + '$let' => [ + 'vars' => [ + 'total' => ['$add' => ['$price', '$tax']], + 'discounted' => ['$cond' => ['if' => '$applyDiscount', 'then' => 0.9, 'else' => 1]], + ], + 'in' => ['$multiply' => ['$$total', '$$discounted']], + ], ], - 'toString' => [ - 'expected' => ['$toString' => '$field'], - 'operator' => 'toString', - 'args' => ['$field'], + 'operator' => 'let', + 'args' => static fn (Expr $expr) => [ + $expr->expr() + ->field('total') + ->add('$price', '$tax') + ->field('discounted') + ->cond('$applyDiscount', 0.9, 1), + $expr->expr() + ->multiply('$$total', '$$discounted'), ], ]; + + yield 'literal' => [ + 'expected' => ['$literal' => '$field'], + 'operator' => 'literal', + 'args' => ['$field'], + ]; + + yield 'meta' => [ + 'expected' => ['$meta' => '$field'], + 'operator' => 'meta', + 'args' => ['$field'], + ]; + + yield 'rand' => [ + 'expected' => ['$rand' => []], + 'operator' => 'rand', + 'args' => [], + ]; + + yield 'sampleRate' => [ + 'expected' => ['$sampleRate' => 0.5], + 'operator' => 'sampleRate', + 'args' => [0.5], + ]; + } + + public static function provideObjectExpressionOperators(): Generator + { + yield 'getField' => [ + 'expected' => ['$getField' => ['field' => '$field', 'input' => '$obj']], + 'operator' => 'getField', + 'args' => ['$field', '$obj'], + ]; + + yield 'getFieldWithoutObject' => [ + 'expected' => ['$getField' => ['field' => '$field']], + 'operator' => 'getField', + 'args' => ['$field'], + ]; + + yield 'mergeObjectsWithTwoArgs' => [ + 'expected' => ['$mergeObjects' => ['$obj1', '$obj2']], + 'operator' => 'mergeObjects', + 'args' => ['$obj1', '$obj2'], + ]; + + yield 'mergeObjectsWithMultipleArgs' => [ + 'expected' => ['$mergeObjects' => ['$obj1', '$obj2', '$obj3']], + 'operator' => 'mergeObjects', + 'args' => ['$obj1', '$obj2', '$obj3'], + ]; + + yield 'objectToArray' => [ + 'expected' => ['$objectToArray' => ['$obj']], + 'operator' => 'objectToArray', + 'args' => [['$obj']], + ]; + + yield 'setField' => [ + 'expected' => ['$setField' => ['field' => '$field', 'input' => '$obj', 'value' => 5]], + 'operator' => 'setField', + 'args' => ['$field', '$obj', 5], + ]; + } + + public static function provideSetExpressionOperators(): Generator + { + yield 'allElementsTrue' => [ + 'expected' => ['$allElementsTrue' => '$field'], + 'operator' => 'allElementsTrue', + 'args' => ['$field'], + ]; + + yield 'anyElementTrue' => [ + 'expected' => ['$anyElementTrue' => '$field'], + 'operator' => 'anyElementTrue', + 'args' => ['$field'], + ]; + + yield 'setDifference' => [ + 'expected' => ['$setDifference' => ['$field', '$otherField']], + 'operator' => 'setDifference', + 'args' => ['$field', '$otherField'], + ]; + + yield 'setEqualsWithTwoSets' => [ + 'expected' => ['$setEquals' => ['$set1', '$set2']], + 'operator' => 'setEquals', + 'args' => ['$set1', '$set2'], + ]; + + yield 'setEqualsWithMultipleSets' => [ + 'expected' => ['$setEquals' => ['$set1', '$set2', '$set3', '$set4']], + 'operator' => 'setEquals', + 'args' => ['$set1', '$set2', '$set3', '$set4'], + ]; + + yield 'setIntersectionWithTwoSets' => [ + 'expected' => ['$setIntersection' => ['$set1', '$set2']], + 'operator' => 'setIntersection', + 'args' => ['$set1', '$set2'], + ]; + + yield 'setIntersectionWithMultipleSets' => [ + 'expected' => ['$setIntersection' => ['$set1', '$set2', '$set3', '$set4']], + 'operator' => 'setIntersection', + 'args' => ['$set1', '$set2', '$set3', '$set4'], + ]; + + yield 'setIsSubset' => [ + 'expected' => ['$setIsSubset' => ['$field', '$otherField']], + 'operator' => 'setIsSubset', + 'args' => ['$field', '$otherField'], + ]; + + yield 'setUnionWithTwoSets' => [ + 'expected' => ['$setUnion' => ['$set1', '$set2']], + 'operator' => 'setUnion', + 'args' => ['$set1', '$set2'], + ]; + + yield 'setUnionWithMultipleSets' => [ + 'expected' => ['$setUnion' => ['$set1', '$set2', '$set3', '$set4']], + 'operator' => 'setUnion', + 'args' => ['$set1', '$set2', '$set3', '$set4'], + ]; + } + + public static function provideStringExpressionOperators(): Generator + { + yield 'concatWithTwoArgs' => [ + 'expected' => ['$concat' => ['foo', '$field']], + 'operator' => 'concat', + 'args' => ['foo', '$field'], + ]; + + yield 'concatWithMultipleArgs' => [ + 'expected' => ['$concat' => ['foo', '$field', '$otherField', 'bleh']], + 'operator' => 'concat', + 'args' => ['foo', '$field', '$otherField', 'bleh'], + ]; + + yield 'indexOfBytesWithoutStartOrEnd' => [ + 'expected' => ['$indexOfBytes' => ['$field', '$otherField']], + 'operator' => 'indexOfBytes', + 'args' => ['$field', '$otherField'], + ]; + + yield 'indexOfBytesWithoutStartWithEnd' => [ + 'expected' => ['$indexOfBytes' => ['$field', '$otherField']], + 'operator' => 'indexOfBytes', + 'args' => ['$field', '$otherField', null, '$end'], + ]; + + yield 'indexOfBytesWithStart' => [ + 'expected' => ['$indexOfBytes' => ['$field', '$otherField', '$start']], + 'operator' => 'indexOfBytes', + 'args' => ['$field', '$otherField', '$start'], + ]; + + yield 'indexOfBytesWithStartAndEnd' => [ + 'expected' => ['$indexOfBytes' => ['$field', '$otherField', '$start', '$end']], + 'operator' => 'indexOfBytes', + 'args' => ['$field', '$otherField', '$start', '$end'], + ]; + + yield 'indexOfCPWithoutStartOrEnd' => [ + 'expected' => ['$indexOfCP' => ['$field', '$otherField']], + 'operator' => 'indexOfCP', + 'args' => ['$field', '$otherField'], + ]; + + yield 'indexOfCPWithoutStartWithEnd' => [ + 'expected' => ['$indexOfCP' => ['$field', '$otherField']], + 'operator' => 'indexOfCP', + 'args' => ['$field', '$otherField', null, '$end'], + ]; + + yield 'indexOfCPWithStart' => [ + 'expected' => ['$indexOfCP' => ['$field', '$otherField', '$start']], + 'operator' => 'indexOfCP', + 'args' => ['$field', '$otherField', '$start'], + ]; + + yield 'indexOfCPWithStartAndEnd' => [ + 'expected' => ['$indexOfCP' => ['$field', '$otherField', '$start', '$end']], + 'operator' => 'indexOfCP', + 'args' => ['$field', '$otherField', '$start', '$end'], + ]; + + yield 'ltrim' => [ + 'expected' => ['$ltrim' => ['$input', '$chars']], + 'operator' => 'ltrim', + 'args' => ['$input', '$chars'], + ]; + + yield 'regexFind' => [ + 'expected' => ['$regexFind' => ['input' => '$input', 'regex' => '$regex']], + 'operator' => 'regexFind', + 'args' => ['$input', '$regex'], + ]; + + yield 'regexFindWithOptions' => [ + 'expected' => ['$regexFind' => ['input' => '$input', 'regex' => '$regex', 'options' => 'i']], + 'operator' => 'regexFind', + 'args' => ['$input', '$regex', 'i'], + ]; + + yield 'regexFindAll' => [ + 'expected' => ['$regexFindAll' => ['input' => '$input', 'regex' => '$regex']], + 'operator' => 'regexFindAll', + 'args' => ['$input', '$regex'], + ]; + + yield 'regexFindAllWithOptions' => [ + 'expected' => ['$regexFindAll' => ['input' => '$input', 'regex' => '$regex', 'options' => 'i']], + 'operator' => 'regexFindAll', + 'args' => ['$input', '$regex', 'i'], + ]; + + yield 'regexMatch' => [ + 'expected' => ['$regexMatch' => ['input' => '$input', 'regex' => '$regex']], + 'operator' => 'regexMatch', + 'args' => ['$input', '$regex'], + ]; + + yield 'regexMatchWithOptions' => [ + 'expected' => ['$regexMatch' => ['input' => '$input', 'regex' => '$regex', 'options' => 'i']], + 'operator' => 'regexMatch', + 'args' => ['$input', '$regex', 'i'], + ]; + + yield 'replaceOne' => [ + 'expected' => ['$replaceOne' => ['input' => '$input', 'find' => '$regex', 'replacement' => 'foo']], + 'operator' => 'replaceOne', + 'args' => ['$input', '$regex', 'foo'], + ]; + + yield 'replaceAll' => [ + 'expected' => ['$replaceAll' => ['input' => '$input', 'find' => '$regex', 'replacement' => 'foo']], + 'operator' => 'replaceAll', + 'args' => ['$input', '$regex', 'foo'], + ]; + + yield 'rtrim' => [ + 'expected' => ['$rtrim' => ['$input', '$chars']], + 'operator' => 'rtrim', + 'args' => ['$input', '$chars'], + ]; + + yield 'split' => [ + 'expected' => ['$split' => ['$string', '$delimiter']], + 'operator' => 'split', + 'args' => ['$string', '$delimiter'], + ]; + + yield 'strcasecmp' => [ + 'expected' => ['$strcasecmp' => ['$field', '$otherField']], + 'operator' => 'strcasecmp', + 'args' => ['$field', '$otherField'], + ]; + + yield 'strLenBytes' => [ + 'expected' => ['$strLenBytes' => '$field'], + 'operator' => 'strLenBytes', + 'args' => ['$field'], + ]; + + yield 'strLenCP' => [ + 'expected' => ['$strLenCP' => '$field'], + 'operator' => 'strLenCP', + 'args' => ['$field'], + ]; + + yield 'substr' => [ + 'expected' => ['$substr' => ['$field', 0, '$length']], + 'operator' => 'substr', + 'args' => ['$field', 0, '$length'], + ]; + + yield 'substrBytes' => [ + 'expected' => ['$substrBytes' => ['$field', 0, '$length']], + 'operator' => 'substrBytes', + 'args' => ['$field', 0, '$length'], + ]; + + yield 'substrCP' => [ + 'expected' => ['$substrCP' => ['$field', 0, '$length']], + 'operator' => 'substrCP', + 'args' => ['$field', 0, '$length'], + ]; + + yield 'toLower' => [ + 'expected' => ['$toLower' => '$field'], + 'operator' => 'toLower', + 'args' => ['$field'], + ]; + + yield 'toUpper' => [ + 'expected' => ['$toUpper' => '$field'], + 'operator' => 'toUpper', + 'args' => ['$field'], + ]; + + yield 'trim' => [ + 'expected' => ['$trim' => ['$input', '$chars']], + 'operator' => 'trim', + 'args' => ['$input', '$chars'], + ]; + } + + public static function provideTimestampExpressionOperators(): Generator + { + yield 'tsIncrement' => [ + 'expected' => ['$tsIncrement' => '$field'], + 'operator' => 'tsIncrement', + 'args' => ['$field'], + ]; + + yield 'tsSecond' => [ + 'expected' => ['$tsSecond' => '$field'], + 'operator' => 'tsSecond', + 'args' => ['$field'], + ]; + } + + public static function provideTrigonometryExpressionOperators(): Generator + { + yield 'sin' => [ + 'expected' => ['$sin' => '$field'], + 'operator' => 'sin', + 'args' => ['$field'], + ]; + + yield 'cos' => [ + 'expected' => ['$cos' => '$field'], + 'operator' => 'cos', + 'args' => ['$field'], + ]; + + yield 'tan' => [ + 'expected' => ['$tan' => '$field'], + 'operator' => 'tan', + 'args' => ['$field'], + ]; + + yield 'asin' => [ + 'expected' => ['$asin' => '$field'], + 'operator' => 'asin', + 'args' => ['$field'], + ]; + + yield 'acos' => [ + 'expected' => ['$acos' => '$field'], + 'operator' => 'acos', + 'args' => ['$field'], + ]; + + yield 'atan' => [ + 'expected' => ['$atan' => '$field'], + 'operator' => 'atan', + 'args' => ['$field'], + ]; + + yield 'atan2' => [ + 'expected' => ['$atan2' => ['$expr1', '$expr2']], + 'operator' => 'atan2', + 'args' => ['$expr1', '$expr2'], + ]; + + yield 'sinh' => [ + 'expected' => ['$sinh' => '$field'], + 'operator' => 'sinh', + 'args' => ['$field'], + ]; + + yield 'cosh' => [ + 'expected' => ['$cosh' => '$field'], + 'operator' => 'cosh', + 'args' => ['$field'], + ]; + + yield 'tanh' => [ + 'expected' => ['$tanh' => '$field'], + 'operator' => 'tanh', + 'args' => ['$field'], + ]; + + yield 'asinh' => [ + 'expected' => ['$asinh' => '$field'], + 'operator' => 'asinh', + 'args' => ['$field'], + ]; + + yield 'acosh' => [ + 'expected' => ['$acosh' => '$field'], + 'operator' => 'acosh', + 'args' => ['$field'], + ]; + + yield 'atanh' => [ + 'expected' => ['$atanh' => '$field'], + 'operator' => 'atanh', + 'args' => ['$field'], + ]; + + yield 'degreesToRadians' => [ + 'expected' => ['$degreesToRadians' => '$field'], + 'operator' => 'degreesToRadians', + 'args' => ['$field'], + ]; + + yield 'radiansToDegrees' => [ + 'expected' => ['$radiansToDegrees' => '$field'], + 'operator' => 'radiansToDegrees', + 'args' => ['$field'], + ]; + } + + public static function provideTypeExpressionOperators(): Generator + { + yield 'isArray' => [ + 'expected' => ['$isArray' => '$field'], + 'operator' => 'isArray', + 'args' => ['$field'], + ]; + + yield 'type' => [ + 'expected' => ['$type' => '$field'], + 'operator' => 'type', + 'args' => ['$field'], + ]; + + yield 'convert' => [ + 'expected' => ['$convert' => ['input' => '$field', 'to' => '$to']], + 'operator' => 'convert', + 'args' => ['$field', '$to', null, null], + ]; + + yield 'isNumber' => [ + 'expected' => ['$isNumber' => '$field'], + 'operator' => 'isNumber', + 'args' => ['$field'], + ]; + + yield 'toBool' => [ + 'expected' => ['$toBool' => '$field'], + 'operator' => 'toBool', + 'args' => ['$field'], + ]; + + yield 'toDate' => [ + 'expected' => ['$toDate' => '$field'], + 'operator' => 'toDate', + 'args' => ['$field'], + ]; + + yield 'toDecimal' => [ + 'expected' => ['$toDecimal' => '$field'], + 'operator' => 'toDecimal', + 'args' => ['$field'], + ]; + + yield 'toDouble' => [ + 'expected' => ['$toDouble' => '$field'], + 'operator' => 'toDouble', + 'args' => ['$field'], + ]; + + yield 'toInt' => [ + 'expected' => ['$toInt' => '$field'], + 'operator' => 'toInt', + 'args' => ['$field'], + ]; + + yield 'toLong' => [ + 'expected' => ['$toLong' => '$field'], + 'operator' => 'toLong', + 'args' => ['$field'], + ]; + + yield 'toObjectId' => [ + 'expected' => ['$toObjectId' => '$field'], + 'operator' => 'toObjectId', + 'args' => ['$field'], + ]; + + yield 'toString' => [ + 'expected' => ['$toString' => '$field'], + 'operator' => 'toString', + 'args' => ['$field'], + ]; } protected function createExpr(): Expr diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/BuilderTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/BuilderTest.php index 701b38a9e4..0fc3dad8b1 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/BuilderTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/BuilderTest.php @@ -285,7 +285,7 @@ public function testPipelineConvertsTypes(): void '$group' => [ '_id' => [ '$cond' => [ - 'if' => ['$lt' => ['$createdAt', new UTCDateTime((int) $dateTime->format('Uv'))]], + 'if' => ['$lt' => ['$createdAt', new UTCDateTime($dateTime)]], 'then' => true, 'else' => false, ], @@ -295,9 +295,9 @@ public function testPipelineConvertsTypes(): void ], [ '$replaceRoot' => [ - 'newRoot' => (object) [ + 'newRoot' => [ 'isToday' => [ - '$eq' => ['$createdAt', new UTCDateTime((int) $dateTime->format('Uv'))], + '$eq' => ['$createdAt', new UTCDateTime($dateTime)], ], ], ], diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/ExprTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/ExprTest.php index 2c8de1170b..40471cf172 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/ExprTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/ExprTest.php @@ -18,7 +18,23 @@ class ExprTest extends BaseTestCase * @param array $expected * @param Closure(Expr): mixed[]|mixed[] $args * - * @dataProvider provideAllOperators + * @dataProvider provideAccumulatorExpressionOperators + * @dataProvider provideArithmeticExpressionOperators + * @dataProvider provideArrayExpressionOperators + * @dataProvider provideBooleanExpressionOperators + * @dataProvider provideComparisonExpressionOperators + * @dataProvider provideConditionalExpressionOperators + * @dataProvider provideCustomExpressionOperators + * @dataProvider provideDataSizeExpressionOperators + * @dataProvider provideDateExpressionOperators + * @dataProvider provideGroupAccumulatorExpressionOperators + * @dataProvider provideMiscExpressionOperators + * @dataProvider provideObjectExpressionOperators + * @dataProvider provideSetExpressionOperators + * @dataProvider provideStringExpressionOperators + * @dataProvider provideTimestampExpressionOperators + * @dataProvider provideTrigonometryExpressionOperators + * @dataProvider provideTypeExpressionOperators */ public function testGenericOperator(array $expected, string $operator, $args): void { @@ -33,7 +49,23 @@ public function testGenericOperator(array $expected, string $operator, $args): v * @param array $expected * @param Closure(Expr): mixed[]|mixed[] $args * - * @dataProvider provideAllOperators + * @dataProvider provideAccumulatorExpressionOperators + * @dataProvider provideArithmeticExpressionOperators + * @dataProvider provideArrayExpressionOperators + * @dataProvider provideBooleanExpressionOperators + * @dataProvider provideComparisonExpressionOperators + * @dataProvider provideConditionalExpressionOperators + * @dataProvider provideCustomExpressionOperators + * @dataProvider provideDataSizeExpressionOperators + * @dataProvider provideDateExpressionOperators + * @dataProvider provideGroupAccumulatorExpressionOperators + * @dataProvider provideMiscExpressionOperators + * @dataProvider provideObjectExpressionOperators + * @dataProvider provideSetExpressionOperators + * @dataProvider provideStringExpressionOperators + * @dataProvider provideTimestampExpressionOperators + * @dataProvider provideTrigonometryExpressionOperators + * @dataProvider provideTypeExpressionOperators */ public function testGenericOperatorWithField(array $expected, string $operator, $args): void { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/AddFieldsTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/AddFieldsTest.php index 77fadb63ac..72e2513bb7 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/AddFieldsTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/AddFieldsTest.php @@ -12,7 +12,7 @@ class AddFieldsTest extends BaseTestCase { use AggregationTestTrait; - public function testAddFieldsStage(): void + public function testStage(): void { $addFieldsStage = new AddFields($this->getTestAggregationBuilder()); $addFieldsStage @@ -22,7 +22,7 @@ public function testAddFieldsStage(): void self::assertSame(['$addFields' => ['product' => ['$multiply' => ['$field', 5]]]], $addFieldsStage->getExpression()); } - public function testProjectFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/BucketAutoTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/BucketAutoTest.php index 19bdf02b09..d390c81d86 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/BucketAutoTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/BucketAutoTest.php @@ -4,8 +4,11 @@ namespace Doctrine\ODM\MongoDB\Tests\Aggregation\Stage; +use Closure; +use Doctrine\ODM\MongoDB\Aggregation\Expr; use Doctrine\ODM\MongoDB\Aggregation\Stage\BucketAuto; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Tests\Aggregation\AggregationOperatorsProviderTrait; use Doctrine\ODM\MongoDB\Tests\Aggregation\AggregationTestTrait; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\CmsComment; @@ -13,10 +16,19 @@ class BucketAutoTest extends BaseTestCase { + use AggregationOperatorsProviderTrait; use AggregationTestTrait; - public function testBucketAutoStage(): void + /** + * @param array $expected + * @param mixed[]|Closure(Expr): mixed[] $args + * + * @dataProvider provideGroupAccumulatorExpressionOperators + */ + public function testGroupAccumulators(array $expected, string $operator, $args): void { + $args = $this->resolveArgs($args); + $bucketStage = new BucketAuto($this->getTestAggregationBuilder(), $this->dm, new ClassMetadata(User::class)); $bucketStage ->groupBy('$someField') @@ -24,19 +36,19 @@ public function testBucketAutoStage(): void ->granularity('R10') ->output() ->field('averageValue') - ->avg('$value'); + ->$operator(...$args); self::assertSame([ '$bucketAuto' => [ 'groupBy' => '$someField', 'buckets' => 3, 'granularity' => 'R10', - 'output' => ['averageValue' => ['$avg' => '$value']], + 'output' => ['averageValue' => $expected], ], ], $bucketStage->getExpression()); } - public function testBucketAutoFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->bucketAuto() @@ -59,7 +71,7 @@ public function testBucketAutoFromBuilder(): void ], $builder->getPipeline()); } - public function testBucketAutoSkipsUndefinedProperties(): void + public function testSkipsUndefinedProperties(): void { $bucketStage = new BucketAuto($this->getTestAggregationBuilder(), $this->dm, new ClassMetadata(User::class)); $bucketStage diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/BucketTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/BucketTest.php index 6967db04df..476c540c2d 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/BucketTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/BucketTest.php @@ -4,8 +4,11 @@ namespace Doctrine\ODM\MongoDB\Tests\Aggregation\Stage; +use Closure; +use Doctrine\ODM\MongoDB\Aggregation\Expr; use Doctrine\ODM\MongoDB\Aggregation\Stage\Bucket; use Doctrine\ODM\MongoDB\Mapping\ClassMetadata; +use Doctrine\ODM\MongoDB\Tests\Aggregation\AggregationOperatorsProviderTrait; use Doctrine\ODM\MongoDB\Tests\Aggregation\AggregationTestTrait; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\CmsComment; @@ -13,10 +16,19 @@ class BucketTest extends BaseTestCase { + use AggregationOperatorsProviderTrait; use AggregationTestTrait; - public function testBucketStage(): void + /** + * @param array $expected + * @param mixed[]|Closure(Expr): mixed[] $args + * + * @dataProvider provideGroupAccumulatorExpressionOperators + */ + public function testGroupAccumulators(array $expected, string $operator, $args): void { + $args = $this->resolveArgs($args); + $bucketStage = new Bucket($this->getTestAggregationBuilder(), $this->dm, new ClassMetadata(User::class)); $bucketStage ->groupBy('$someField') @@ -24,19 +36,19 @@ public function testBucketStage(): void ->defaultBucket(0) ->output() ->field('averageValue') - ->avg('$value'); + ->$operator(...$args); self::assertSame([ '$bucket' => [ 'groupBy' => '$someField', 'boundaries' => [1, 2, 3], 'default' => 0, - 'output' => ['averageValue' => ['$avg' => '$value']], + 'output' => ['averageValue' => $expected], ], ], $bucketStage->getExpression()); } - public function testBucketFromBuilder(): void + public function testBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->bucket() @@ -59,7 +71,7 @@ public function testBucketFromBuilder(): void ], $builder->getPipeline()); } - public function testBucketSkipsUndefinedProperties(): void + public function testSkipsUndefinedProperties(): void { $bucketStage = new Bucket($this->getTestAggregationBuilder(), $this->dm, new ClassMetadata(User::class)); $bucketStage diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/CollStatsTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/CollStatsTest.php index 596474d25f..1fcc441bd5 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/CollStatsTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/CollStatsTest.php @@ -12,14 +12,14 @@ class CollStatsTest extends BaseTestCase { use AggregationTestTrait; - public function testCollStatsStage(): void + public function testStage(): void { $collStatsStage = new CollStats($this->getTestAggregationBuilder()); self::assertSame(['$collStats' => []], $collStatsStage->getExpression()); } - public function testCollStatsStageWithLatencyStats(): void + public function testStageWithLatencyStats(): void { $collStatsStage = new CollStats($this->getTestAggregationBuilder()); $collStatsStage->showLatencyStats(); @@ -27,7 +27,7 @@ public function testCollStatsStageWithLatencyStats(): void self::assertSame(['$collStats' => ['latencyStats' => ['histograms' => false]]], $collStatsStage->getExpression()); } - public function testCollStatsStageWithLatencyStatsHistograms(): void + public function testStageWithLatencyStatsHistograms(): void { $collStatsStage = new CollStats($this->getTestAggregationBuilder()); $collStatsStage->showLatencyStats(true); @@ -35,7 +35,7 @@ public function testCollStatsStageWithLatencyStatsHistograms(): void self::assertSame(['$collStats' => ['latencyStats' => ['histograms' => true]]], $collStatsStage->getExpression()); } - public function testCollStatsStageWithStorageStats(): void + public function testStageWithStorageStats(): void { $collStatsStage = new CollStats($this->getTestAggregationBuilder()); $collStatsStage->showStorageStats(); @@ -43,7 +43,7 @@ public function testCollStatsStageWithStorageStats(): void self::assertSame(['$collStats' => ['storageStats' => []]], $collStatsStage->getExpression()); } - public function testCollStatsFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->collStats() diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/CountTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/CountTest.php index 5a99d0e308..9addd550aa 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/CountTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/CountTest.php @@ -12,14 +12,14 @@ class CountTest extends BaseTestCase { use AggregationTestTrait; - public function testCountStage(): void + public function testStage(): void { $countStage = new Count($this->getTestAggregationBuilder(), 'document_count'); self::assertSame(['$count' => 'document_count'], $countStage->getExpression()); } - public function testCountFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->count('document_count'); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/DensifyTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/DensifyTest.php new file mode 100644 index 0000000000..1320a883ac --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/DensifyTest.php @@ -0,0 +1,105 @@ +getTestAggregationBuilder(), 'someField'); + $densifyStage + ->partitionByFields('field1', 'field2') + ->range('full', 1); + + self::assertEquals( + [ + '$densify' => (object) [ + 'field' => 'someField', + 'partitionByFields' => ['field1', 'field2'], + 'range' => (object) [ + 'bounds' => 'full', + 'step' => 1, + ], + ], + ], + $densifyStage->getExpression(), + ); + } + + public function testStageWithPartialBounds(): void + { + $densifyStage = new Densify($this->getTestAggregationBuilder(), 'someField'); + $densifyStage + ->partitionByFields('field1', 'field2') + ->range([1.5, 2.5], 0.1); + + self::assertEquals( + [ + '$densify' => (object) [ + 'field' => 'someField', + 'partitionByFields' => ['field1', 'field2'], + 'range' => (object) [ + 'bounds' => [1.5, 2.5], + 'step' => 0.1, + ], + ], + ], + $densifyStage->getExpression(), + ); + } + + public function testStageWithRangeUnit(): void + { + $densifyStage = new Densify($this->getTestAggregationBuilder(), 'someField'); + $densifyStage + ->partitionByFields('field1', 'field2') + ->range('full', 1, 'minute'); + + self::assertEquals( + [ + '$densify' => (object) [ + 'field' => 'someField', + 'partitionByFields' => ['field1', 'field2'], + 'range' => (object) [ + 'bounds' => 'full', + 'step' => 1, + 'unit' => 'minute', + ], + ], + ], + $densifyStage->getExpression(), + ); + } + + public function testFromBuilder(): void + { + $builder = $this->getTestAggregationBuilder(); + $builder + ->densify('someField') + ->range('full', 1, 'minute'); + + self::assertEquals( + [ + [ + '$densify' => (object) [ + 'field' => 'someField', + 'range' => (object) [ + 'bounds' => 'full', + 'step' => 1, + 'unit' => 'minute', + ], + ], + ], + ], + $builder->getPipeline(), + ); + } +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php index 7714cde75f..d2b0ee0506 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FacetTest.php @@ -15,7 +15,7 @@ class FacetTest extends BaseTestCase { use AggregationTestTrait; - public function testFacetStage(): void + public function testStage(): void { $nestedBuilder = $this->getTestAggregationBuilder(); $nestedBuilder->sortByCount('$tags'); @@ -35,7 +35,7 @@ public function testFacetStage(): void ], $facetStage->getExpression()); } - public function testFacetFromBuilder(): void + public function testFromBuilder(): void { $nestedBuilder = $this->getTestAggregationBuilder(); $nestedBuilder->sortByCount('$tags'); @@ -57,17 +57,17 @@ public function testFacetFromBuilder(): void ], $builder->getPipeline()); } - public function testFacetThrowsExceptionWithoutFieldName(): void + public function testThrowsExceptionWithoutFieldName(): void { $facetStage = new Facet($this->getTestAggregationBuilder()); $this->expectException(LogicException::class); - $this->expectExceptionMessage('requires you set a current field using field().'); + $this->expectExceptionMessage('requires setting a current field using field().'); $facetStage->pipeline($this->getTestAggregationBuilder()); } /** @psalm-suppress InvalidArgument on purpose to throw exception */ - public function testFacetThrowsExceptionOnInvalidPipeline(): void + public function testThrowsExceptionOnInvalidPipeline(): void { $facetStage = new Facet($this->getTestAggregationBuilder()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FillTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FillTest.php new file mode 100644 index 0000000000..0b1a9353c6 --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/FillTest.php @@ -0,0 +1,125 @@ +getTestAggregationBuilder(); + $fillStage = new Fill($builder); + $fillStage + ->partitionByFields('field1', 'field2') + ->sortBy('field1', 1) + ->output() + ->field('foo')->locf() + ->field('bar')->linear() + ->field('fixed')->value(0) + ->field('computed')->value( + $builder->expr()->multiply('$value', 5), + ); + + self::assertEquals( + [ + '$fill' => (object) [ + 'partitionByFields' => ['field1', 'field2'], + 'sortBy' => (object) ['field1' => 1], + 'output' => (object) [ + 'foo' => ['method' => 'locf'], + 'bar' => ['method' => 'linear'], + 'fixed' => ['value' => 0], + 'computed' => ['value' => ['$multiply' => ['$value', 5]]], + ], + ], + ], + $fillStage->getExpression(), + ); + } + + public function testStageWithExpressionAsPartition(): void + { + $builder = $this->getTestAggregationBuilder(); + $fillStage = new Fill($builder); + $fillStage + ->partitionBy($builder->expr()->year('$field')) + ->sortBy('field1', 1) + ->output() + ->field('foo')->locf(); + + self::assertEquals( + [ + '$fill' => (object) [ + 'partitionBy' => ['$year' => '$field'], + 'sortBy' => (object) ['field1' => 1], + 'output' => (object) [ + 'foo' => ['method' => 'locf'], + ], + ], + ], + $fillStage->getExpression(), + ); + } + + public function testStageWithComplexSort(): void + { + $fillStage = new Fill($this->getTestAggregationBuilder()); + $fillStage + ->partitionByFields('field1', 'field2') + ->sortBy(['field1' => 'asc', 'field2' => 'desc']) + ->output() + ->field('foo')->locf(); + + self::assertEquals( + [ + '$fill' => (object) [ + 'partitionByFields' => ['field1', 'field2'], + 'sortBy' => (object) [ + 'field1' => 1, + 'field2' => -1, + ], + 'output' => (object) [ + 'foo' => ['method' => 'locf'], + ], + ], + ], + $fillStage->getExpression(), + ); + } + + public function testFromBuilder(): void + { + $builder = $this->getTestAggregationBuilder(); + $builder->fill() + ->partitionBy('$field1') + ->sortBy('field1', 1) + ->output() + ->field('foo')->locf() + ->field('bar')->linear() + ->field('fixed')->value(0); + + self::assertEquals( + [ + [ + '$fill' => (object) [ + 'partitionBy' => '$field1', + 'sortBy' => (object) ['field1' => 1], + 'output' => (object) [ + 'foo' => ['method' => 'locf'], + 'bar' => ['method' => 'linear'], + 'fixed' => ['value' => 0], + ], + ], + ], + ], + $builder->getPipeline(), + ); + } +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GeoNearTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GeoNearTest.php index 2a272d4545..5c7cb58dca 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GeoNearTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GeoNearTest.php @@ -12,7 +12,7 @@ class GeoNearTest extends BaseTestCase { use AggregationTestTrait; - public function testGeoNearStage(): void + public function testStage(): void { $geoNearStage = new GeoNear($this->getTestAggregationBuilder(), 0, 0); $geoNearStage @@ -24,7 +24,7 @@ public function testGeoNearStage(): void self::assertSame(['$geoNear' => $stage], $geoNearStage->getExpression()); } - public function testGeoNearFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GraphLookupTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GraphLookupTest.php index 3b5cc76bb0..ed9f3d2157 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GraphLookupTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GraphLookupTest.php @@ -24,7 +24,7 @@ class GraphLookupTest extends BaseTestCase { use AggregationTestTrait; - public function testGraphLookupStage(): void + public function testStage(): void { $graphLookupStage = new GraphLookup($this->getTestAggregationBuilder(), 'employees', $this->dm, new ClassMetadata(User::class)); $graphLookupStage @@ -48,7 +48,7 @@ public function testGraphLookupStage(): void ); } - public function testGraphLookupFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->graphLookup('employees') @@ -74,7 +74,7 @@ public function testGraphLookupFromBuilder(): void ); } - public function testGraphLookupWithMatch(): void + public function testWithMatch(): void { $builder = $this->getTestAggregationBuilder(); $builder->graphLookup('employees') @@ -152,7 +152,7 @@ public static function provideEmployeeAggregations(): array * * @dataProvider provideEmployeeAggregations */ - public function testGraphLookupWithEmployees(Closure $addGraphLookupStage, array $expectedFields): void + public function testWithEmployees(Closure $addGraphLookupStage, array $expectedFields): void { $this->insertEmployeeTestData(); @@ -225,7 +225,7 @@ public static function provideTravellerAggregations(): array * * @dataProvider provideTravellerAggregations */ - public function testGraphLookupWithTraveller(Closure $addGraphLookupStage, array $expectedFields): void + public function testWithTraveller(Closure $addGraphLookupStage, array $expectedFields): void { $this->insertTravellerTestData(); @@ -251,7 +251,7 @@ public function testGraphLookupWithTraveller(Closure $addGraphLookupStage, array self::assertCount(3, $result); } - public function testGraphLookupWithUnmappedFields(): void + public function testWithUnmappedFields(): void { $builder = $this->dm->createAggregationBuilder(User::class); @@ -278,7 +278,7 @@ public function testGraphLookupWithUnmappedFields(): void self::assertEquals($expectedPipeline, $builder->getPipeline()); } - public function testGraphLookupWithconnectFromFieldToDifferentTargetClass(): void + public function testWithconnectFromFieldToDifferentTargetClass(): void { $builder = $this->dm->createAggregationBuilder(User::class); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GroupTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GroupTest.php index 4bcea4f5ff..539b0886dc 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GroupTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/GroupTest.php @@ -17,60 +17,21 @@ class GroupTest extends BaseTestCase use AggregationOperatorsProviderTrait; /** - * @param Closure(Expr): Expr[]|mixed[] $args + * @param array $expected + * @param mixed[]|Closure(Expr): mixed[] $args * - * @dataProvider provideProxiedExprMethods + * @dataProvider provideGroupAccumulatorExpressionOperators */ - public function testProxiedExprMethods(string $method, $args = []): void + public function testGroupAccumulators(array $expected, string $operator, $args): void { - $args = $this->resolveArgs($args); - - $expr = $this->getMockAggregationExpr(); - $expr - ->expects($this->once()) - ->method($method) - ->with(...$args); - - $stage = new class ($this->getTestAggregationBuilder()) extends Group { - public function setExpr(Expr $expr): void - { - $this->expr = $expr; - } - }; - $stage->setExpr($expr); - - self::assertSame($stage, $stage->$method(...$args)); - } - - public static function provideProxiedExprMethods(): array - { - return [ - 'addToSet()' => ['addToSet', ['$field']], - 'avg()' => ['avg', ['$field']], - 'expression()' => [ - 'expression', - static function (Expr $expr) { - $expr - ->field('dayOfMonth') - ->dayOfMonth('$dateField') - ->field('dayOfWeek') - ->dayOfWeek('$dateField'); + $groupStage = new Group($this->getTestAggregationBuilder()); + $args = $this->resolveArgs($args); - return [$expr]; - }, - ], - 'first()' => ['first', ['$field']], - 'last()' => ['last', ['$field']], - 'max()' => ['max', ['$field']], - 'min()' => ['min', ['$field']], - 'push()' => ['push', ['$field']], - 'stdDevPop()' => ['stdDevPop', ['$field']], - 'stdDevSamp()' => ['stdDevSamp', ['$field']], - 'sum()' => ['sum', ['$field']], - ]; + self::assertSame($groupStage, $groupStage->field('foo')->$operator(...$args)); + self::assertSame(['$group' => ['foo' => $expected]], $groupStage->getExpression()); } - public function testGroupStage(): void + public function testStage(): void { $groupStage = new Group($this->getTestAggregationBuilder()); $groupStage @@ -82,7 +43,7 @@ public function testGroupStage(): void self::assertSame(['$group' => ['_id' => '$field', 'count' => ['$sum' => 1]]], $groupStage->getExpression()); } - public function testGroupFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder @@ -95,7 +56,7 @@ public function testGroupFromBuilder(): void self::assertSame([['$group' => ['_id' => '$field', 'count' => ['$sum' => 1]]]], $builder->getPipeline()); } - public function testGroupWithOperatorInId(): void + public function testWithOperatorInId(): void { $groupStage = new Group($this->getTestAggregationBuilder()); $groupStage diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/IndexStatsTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/IndexStatsTest.php index 2c8d5bdf9b..673c12ef9d 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/IndexStatsTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/IndexStatsTest.php @@ -13,14 +13,14 @@ class IndexStatsTest extends BaseTestCase { use AggregationTestTrait; - public function testIndexStatsStage(): void + public function testStage(): void { $indexStatsStage = new IndexStats($this->getTestAggregationBuilder()); self::assertEquals(['$indexStats' => new stdClass()], $indexStatsStage->getExpression()); } - public function testIndexStatsFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->indexStats(); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/LimitTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/LimitTest.php index d8d3f6455d..37a57bdb4d 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/LimitTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/LimitTest.php @@ -12,14 +12,14 @@ class LimitTest extends BaseTestCase { use AggregationTestTrait; - public function testLimitStage(): void + public function testStage(): void { $limitStage = new Limit($this->getTestAggregationBuilder(), 10); self::assertSame(['$limit' => 10], $limitStage->getExpression()); } - public function testLimitFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->limit(10); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/LookupTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/LookupTest.php index 6406ef3e9a..226c8f7ade 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/LookupTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/LookupTest.php @@ -20,7 +20,7 @@ public function setUp(): void $this->insertTestData(); } - public function testLookupStage(): void + public function testStage(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $builder @@ -47,7 +47,7 @@ public function testLookupStage(): void self::assertSame('alcaeus', $result[0]['user'][0]['username']); } - public function testLookupStageWithPipelineAsArray(): void + public function testStageWithPipelineAsArray(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $builder @@ -91,7 +91,7 @@ public function testLookupStageWithPipelineAsArray(): void $this->assertEquals($expectedPipeline, $builder->getPipeline()); } - public function testLookupStageWithPipelineAsStage(): void + public function testStageWithPipelineAsStage(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $lookupPipelineBuilder = $this->dm->createAggregationBuilder(User::class); @@ -133,7 +133,7 @@ public function testLookupStageWithPipelineAsStage(): void $this->assertEquals($expectedPipeline, $builder->getPipeline()); } - public function testLookupThrowsExceptionUsingSameBuilderForPipeline(): void + public function testThrowsExceptionUsingSameBuilderForPipeline(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); @@ -151,7 +151,7 @@ public function testLookupThrowsExceptionUsingSameBuilderForPipeline(): void ); } - public function testLookupStageWithPipelineAndLocalForeignFields(): void + public function testStageWithPipelineAndLocalForeignFields(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $lookupPipelineBuilder = $this->dm->createAggregationBuilder(User::class); @@ -192,7 +192,7 @@ public function testLookupStageWithPipelineAndLocalForeignFields(): void $this->assertEquals($expectedPipeline, $builder->getPipeline()); } - public function testLookupStageWithFieldNameTranslation(): void + public function testStageWithFieldNameTranslation(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $builder @@ -215,7 +215,7 @@ public function testLookupStageWithFieldNameTranslation(): void self::assertEquals($expectedPipeline, $builder->getPipeline()); } - public function testLookupStageWithClassName(): void + public function testStageWithClassName(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $builder @@ -244,7 +244,7 @@ public function testLookupStageWithClassName(): void self::assertSame('alcaeus', $result[0]['user'][0]['username']); } - public function testLookupStageWithCollectionName(): void + public function testStageWithCollectionName(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $builder @@ -272,7 +272,7 @@ public function testLookupStageWithCollectionName(): void self::assertEmpty($result[0]['user']); } - public function testLookupStageReferenceMany(): void + public function testStageReferenceMany(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $builder @@ -303,7 +303,7 @@ public function testLookupStageReferenceMany(): void self::assertSame('malarzm', $result[1]['users'][0]['username']); } - public function testLookupStageReferenceManyStoreAsRef(): void + public function testStageReferenceManyStoreAsRef(): void { $builder = $this->dm->createAggregationBuilder(ReferenceUser::class); $builder @@ -334,7 +334,7 @@ public function testLookupStageReferenceManyStoreAsRef(): void self::assertSame('malarzm', $result[1]['users'][0]['username']); } - public function testLookupStageReferenceOneInverse(): void + public function testStageReferenceOneInverse(): void { $builder = $this->dm->createAggregationBuilder(User::class); $builder @@ -366,7 +366,7 @@ public function testLookupStageReferenceOneInverse(): void self::assertCount(1, $result[0]['simpleReferenceOneInverse']); } - public function testLookupStageReferenceManyInverse(): void + public function testStageReferenceManyInverse(): void { $builder = $this->dm->createAggregationBuilder(User::class); $builder @@ -398,7 +398,7 @@ public function testLookupStageReferenceManyInverse(): void self::assertCount(1, $result[0]['simpleReferenceManyInverse']); } - public function testLookupStageReferenceOneInverseStoreAsRef(): void + public function testStageReferenceOneInverseStoreAsRef(): void { $builder = $this->dm->createAggregationBuilder(User::class); $builder @@ -430,7 +430,7 @@ public function testLookupStageReferenceOneInverseStoreAsRef(): void self::assertCount(1, $result[0]['embeddedReferenceOneInverse']); } - public function testLookupStageReferenceManyInverseStoreAsRef(): void + public function testStageReferenceManyInverseStoreAsRef(): void { $builder = $this->dm->createAggregationBuilder(User::class); $builder @@ -489,7 +489,7 @@ private function insertTestData(): void $this->dm->flush(); } - public function testLookupStageAndDefaultAlias(): void + public function testStageAndDefaultAlias(): void { $builder = $this->dm->createAggregationBuilder(User::class); $builder @@ -512,7 +512,7 @@ public function testLookupStageAndDefaultAlias(): void self::assertCount(1, $result[0]['simpleReferenceOneInverse']); } - public function testLookupStageAndDefaultAliasOverride(): void + public function testStageAndDefaultAliasOverride(): void { $builder = $this->dm->createAggregationBuilder(User::class); $builder diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/MatchStageTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/MatchStageTest.php index cdb20c595d..5107cdad7a 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/MatchStageTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/MatchStageTest.php @@ -6,18 +6,16 @@ use DateTime; use Doctrine\ODM\MongoDB\Aggregation\Stage\MatchStage; -use Doctrine\ODM\MongoDB\Query\Expr; use Doctrine\ODM\MongoDB\Tests\Aggregation\AggregationTestTrait; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\User; -use GeoJson\Geometry\Geometry; use MongoDB\BSON\UTCDateTime; class MatchStageTest extends BaseTestCase { use AggregationTestTrait; - public function testMatchStage(): void + public function testStage(): void { $matchStage = new MatchStage($this->getTestAggregationBuilder()); $matchStage @@ -27,7 +25,7 @@ public function testMatchStage(): void self::assertSame(['$match' => ['someField' => 'someValue']], $matchStage->getExpression()); } - public function testMatchFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder @@ -38,62 +36,6 @@ public function testMatchFromBuilder(): void self::assertSame([['$match' => ['someField' => 'someValue']]], $builder->getPipeline()); } - /** @dataProvider provideProxiedExprMethods */ - public function testProxiedExprMethods(string $method, array $args = []): void - { - $expr = $this->getMockQueryExpr(); - $expr - ->expects($this->once()) - ->method($method) - ->with(...$args); - - $stage = new class ($this->getTestAggregationBuilder()) extends MatchStage { - public function setQuery(Expr $query): void - { - $this->query = $query; - } - }; - $stage->setQuery($expr); - - self::assertSame($stage, $stage->$method(...$args)); - } - - public static function provideProxiedExprMethods(): array - { - return [ - 'field()' => ['field', ['fieldName']], - 'equals()' => ['equals', ['value']], - 'in()' => ['in', [['value1', 'value2']]], - 'notIn()' => ['notIn', [['value1', 'value2']]], - 'notEqual()' => ['notEqual', ['value']], - 'gt()' => ['gt', [1]], - 'gte()' => ['gte', [1]], - 'lt()' => ['gt', [1]], - 'lte()' => ['gte', [1]], - 'range()' => ['range', [0, 1]], - 'size()' => ['size', [1]], - 'exists()' => ['exists', [true]], - 'type()' => ['type', [7]], - 'all()' => ['all', [['value1', 'value2']]], - 'mod()' => ['mod', [2, 0]], - 'geoIntersects()' => ['geoIntersects', [self::createGeometry()]], - 'geoWithin()' => ['geoWithin', [self::createGeometry()]], - 'geoWithinBox()' => ['geoWithinBox', [1, 2, 3, 4]], - 'geoWithinCenter()' => ['geoWithinCenter', [1, 2, 3]], - 'geoWithinCenterSphere()' => ['geoWithinCenterSphere', [1, 2, 3]], - 'geoWithinPolygon()' => ['geoWithinPolygon', [[0, 0], [1, 1], [1, 0]]], - 'addAnd() array' => ['addAnd', [[]]], - 'addAnd() Expr' => ['addAnd', [self::createExpr()]], - 'addOr() array' => ['addOr', [[]]], - 'addOr() Expr' => ['addOr', [self::createExpr()]], - 'addNor() array' => ['addNor', [[]]], - 'addNor() Expr' => ['addNor', [self::createExpr()]], - 'not()' => ['not', [self::createExpr()]], - 'language()' => ['language', ['en']], - 'text()' => ['text', ['foo']], - ]; - } - public function testTypeConversion(): void { $builder = $this->dm->createAggregationBuilder(User::class); @@ -114,15 +56,4 @@ public function testTypeConversion(): void $stage->getExpression(), ); } - - private static function createGeometry(): Geometry - { - return new class extends Geometry { - }; - } - - private static function createExpr(): Expr - { - return new Expr(static::createTestDocumentManager()); - } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/MergeTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/MergeTest.php new file mode 100644 index 0000000000..638f0c195b --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/MergeTest.php @@ -0,0 +1,129 @@ +dm->createAggregationBuilder(SimpleReferenceUser::class); + $builder + ->merge() + ->into(User::class) + ->on('_id') + ->whenMatched('keepExisting'); + + $expectedPipeline = [ + [ + '$merge' => (object) [ + 'into' => 'users', + 'on' => '_id', + 'whenMatched' => 'keepExisting', + ], + ], + ]; + + self::assertEquals($expectedPipeline, $builder->getPipeline()); + } + + public function testStageWithCollectionName(): void + { + $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); + $builder + ->merge() + ->into('someRandomCollectionName') + ->let(['foo' => 'bar']) + ->whenNotMatched('discard'); + + $expectedPipeline = [ + [ + '$merge' => (object) [ + 'into' => 'someRandomCollectionName', + 'let' => ['foo' => 'bar'], + 'whenNotMatched' => 'discard', + ], + ], + ]; + + self::assertEquals($expectedPipeline, $builder->getPipeline()); + } + + public static function providePipeline(): Generator + { + yield 'Array' => [ + 'pipeline' => [ + ['$set' => ['foo' => 'bar']], + ['$unset' => ['bar']], + ], + ]; + + yield 'Builder' => [ + 'pipeline' => static function (Builder $builder): Builder { + $builder + ->set() + ->field('foo')->expression('bar') + ->unset('bar'); + + return $builder; + }, + ]; + } + + /** + * @param array>|callable $pipeline + * + * @dataProvider providePipeline + */ + public function testStageWithPipeline($pipeline): void + { + if (is_callable($pipeline)) { + $pipeline = $pipeline($this->dm->createAggregationBuilder(SimpleReferenceUser::class)); + } + + $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); + $builder + ->merge() + ->into('someRandomCollectionName') + ->whenMatched($pipeline); + + $expectedPipeline = [ + [ + '$merge' => (object) [ + 'into' => 'someRandomCollectionName', + 'whenMatched' => [ + ['$set' => ['foo' => 'bar']], + ['$unset' => ['bar']], + ], + ], + ], + ]; + + self::assertEquals($expectedPipeline, $builder->getPipeline()); + } + + public function testStageWithReusedPipeline(): void + { + $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); + $setStage = $builder->set() + ->field('foo')->expression('bar'); + + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('Cannot use the same Builder instance for $merge whenMatched pipeline.'); + + $builder + ->merge() + ->into('someRandomCollectionName') + ->whenMatched($setStage); + } +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/OperatorTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/OperatorTest.php index 56f8154de8..86874e6958 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/OperatorTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/OperatorTest.php @@ -19,9 +19,23 @@ class OperatorTest extends BaseTestCase /** * @param array $expected - * @param Closure(Expr): mixed[]|mixed[] $args + * @param mixed[]|Closure(Expr): mixed[] $args * - * @dataProvider provideExpressionOperators + * @dataProvider provideAccumulatorExpressionOperators + * @dataProvider provideArithmeticExpressionOperators + * @dataProvider provideArrayExpressionOperators + * @dataProvider provideBooleanExpressionOperators + * @dataProvider provideComparisonExpressionOperators + * @dataProvider provideConditionalExpressionOperators + * @dataProvider provideDataSizeExpressionOperators + * @dataProvider provideDateExpressionOperators + * @dataProvider provideMiscExpressionOperators + * @dataProvider provideObjectExpressionOperators + * @dataProvider provideSetExpressionOperators + * @dataProvider provideStringExpressionOperators + * @dataProvider provideTimestampExpressionOperators + * @dataProvider provideTrigonometryExpressionOperators + * @dataProvider provideTypeExpressionOperators */ public function testProxiedExpressionOperators(array $expected, string $operator, $args): void { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/OutTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/OutTest.php index 5520a70197..83507cc4ca 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/OutTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/OutTest.php @@ -12,7 +12,7 @@ class OutTest extends BaseTestCase { - public function testOutStageWithClassName(): void + public function testStageWithClassName(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $builder @@ -25,7 +25,7 @@ public function testOutStageWithClassName(): void self::assertEquals($expectedPipeline, $builder->getPipeline()); } - public function testOutStageWithCollectionName(): void + public function testStageWithCollectionName(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $builder @@ -38,7 +38,7 @@ public function testOutStageWithCollectionName(): void self::assertEquals($expectedPipeline, $builder->getPipeline()); } - public function testOutStageWithShardedClassName(): void + public function testStageWithShardedClassName(): void { $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); $this->expectException(MappingException::class); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ProjectTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ProjectTest.php index 45a351eb4a..376f48a19b 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ProjectTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ProjectTest.php @@ -4,19 +4,17 @@ namespace Doctrine\ODM\MongoDB\Tests\Aggregation\Stage; -use Doctrine\ODM\MongoDB\Aggregation\Expr; use Doctrine\ODM\MongoDB\Aggregation\Stage\Project; +use Doctrine\ODM\MongoDB\Tests\Aggregation\AggregationOperatorsProviderTrait; use Doctrine\ODM\MongoDB\Tests\Aggregation\AggregationTestTrait; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function array_combine; -use function array_map; - class ProjectTest extends BaseTestCase { + use AggregationOperatorsProviderTrait; use AggregationTestTrait; - public function testProjectStage(): void + public function testStage(): void { $projectStage = new Project($this->getTestAggregationBuilder()); $projectStage @@ -28,7 +26,7 @@ public function testProjectStage(): void self::assertSame(['$project' => ['_id' => false, '$field' => true, '$otherField' => true, 'product' => ['$multiply' => ['$field', 5]]]], $projectStage->getExpression()); } - public function testProjectFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder @@ -41,58 +39,14 @@ public function testProjectFromBuilder(): void self::assertSame([['$project' => ['_id' => false, '$field' => true, '$otherField' => true, 'product' => ['$multiply' => ['$field', 5]]]]], $builder->getPipeline()); } - /** @dataProvider provideAccumulators */ - public function testAccumulatorsWithMultipleArguments(string $operator): void + /** @dataProvider provideAccumulatorExpressionOperators */ + public function testAccumulatorsWithMultipleArguments(array $expected, string $operator, $args): void { $projectStage = new Project($this->getTestAggregationBuilder()); $projectStage ->field('something') - ->$operator('$expression1', '$expression2'); - - self::assertSame(['$project' => ['something' => ['$' . $operator => ['$expression1', '$expression2']]]], $projectStage->getExpression()); - } - - public static function provideAccumulators(): array - { - $operators = ['avg', 'max', 'min', 'stdDevPop', 'stdDevSamp', 'sum']; - - return array_combine($operators, array_map(static fn ($operator) => [$operator], $operators)); - } + ->$operator(...$args); - /** - * @param string[] $args - * - * @dataProvider provideProxiedExprMethods - */ - public function testProxiedExprMethods(string $method, array $args = []): void - { - $expr = $this->getMockAggregationExpr(); - $expr - ->expects($this->once()) - ->method($method) - ->with(...$args); - - $stage = new class ($this->getTestAggregationBuilder()) extends Project { - public function setExpr(Expr $expr): void - { - $this->expr = $expr; - } - }; - $stage->setExpr($expr); - - self::assertSame($stage, $stage->$method(...$args)); - } - - /** @return array */ - public static function provideProxiedExprMethods(): array - { - return [ - 'avg()' => ['avg', ['$field']], - 'max()' => ['max', ['$field']], - 'min()' => ['min', ['$field']], - 'stdDevPop()' => ['stdDevPop', ['$field']], - 'stdDevSamp()' => ['stdDevSamp', ['$field']], - 'sum()' => ['sum', ['$field']], - ]; + self::assertSame(['$project' => ['something' => $expected]], $projectStage->getExpression()); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/RedactTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/RedactTest.php index c6725d3b07..2dd71a7fdf 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/RedactTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/RedactTest.php @@ -12,7 +12,7 @@ class RedactTest extends BaseTestCase { use AggregationTestTrait; - public function testRedactStage(): void + public function testStage(): void { $builder = $this->getTestAggregationBuilder(); @@ -27,7 +27,7 @@ public function testRedactStage(): void self::assertSame(['$redact' => ['$cond' => ['if' => ['$lte' => ['$accessLevel', 3]], 'then' => '$$KEEP', 'else' => '$$REDACT']]], $redactStage->getExpression()); } - public function testRedactFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ReplaceRootTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ReplaceRootTest.php index f419a0d8d7..74b91d410e 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ReplaceRootTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ReplaceRootTest.php @@ -17,7 +17,7 @@ public function testTypeConversion(): void $builder = $this->dm->createAggregationBuilder(User::class); $dateTime = new DateTimeImmutable('2000-01-01T00:00Z'); - $mongoDate = new UTCDateTime((int) $dateTime->format('Uv')); + $mongoDate = new UTCDateTime($dateTime); $stage = $builder ->replaceRoot() ->field('isToday') @@ -26,7 +26,7 @@ public function testTypeConversion(): void self::assertEquals( [ '$replaceRoot' => [ - 'newRoot' => (object) [ + 'newRoot' => [ 'isToday' => ['$eq' => ['$createdAt', $mongoDate]], ], ], @@ -40,7 +40,7 @@ public function testTypeConversionWithDirectExpression(): void $builder = $this->dm->createAggregationBuilder(User::class); $dateTime = new DateTimeImmutable('2000-01-01T00:00Z'); - $mongoDate = new UTCDateTime((int) $dateTime->format('Uv')); + $mongoDate = new UTCDateTime($dateTime); $stage = $builder ->replaceRoot( $builder->expr() @@ -51,7 +51,7 @@ public function testTypeConversionWithDirectExpression(): void self::assertEquals( [ '$replaceRoot' => [ - 'newRoot' => (object) [ + 'newRoot' => [ 'isToday' => ['$eq' => ['$createdAt', $mongoDate]], ], ], @@ -72,7 +72,7 @@ public function testFieldNameConversion(): void self::assertEquals( [ '$replaceRoot' => [ - 'newRoot' => (object) [ + 'newRoot' => [ 'someField' => ['$concat' => ['$ip', 'foo']], ], ], diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ReplaceWithTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ReplaceWithTest.php new file mode 100644 index 0000000000..1f5a71a859 --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/ReplaceWithTest.php @@ -0,0 +1,90 @@ +dm->createAggregationBuilder(User::class); + + $dateTime = new DateTimeImmutable('2000-01-01T00:00Z'); + $mongoDate = new UTCDateTime($dateTime); + $stage = $builder + ->replaceWith() + ->field('isToday') + ->eq('$createdAt', $dateTime); + + self::assertEquals( + [ + '$replaceWith' => [ + 'isToday' => ['$eq' => ['$createdAt', $mongoDate]], + ], + ], + $stage->getExpression(), + ); + } + + public function testTypeConversionWithDirectExpression(): void + { + $builder = $this->dm->createAggregationBuilder(User::class); + + $dateTime = new DateTimeImmutable('2000-01-01T00:00Z'); + $mongoDate = new UTCDateTime($dateTime); + $stage = $builder + ->replaceWith( + $builder->expr() + ->field('isToday') + ->eq('$createdAt', $dateTime), + ); + + self::assertEquals( + [ + '$replaceWith' => [ + 'isToday' => ['$eq' => ['$createdAt', $mongoDate]], + ], + ], + $stage->getExpression(), + ); + } + + public function testFieldNameConversion(): void + { + $builder = $this->dm->createAggregationBuilder(CmsComment::class); + + $stage = $builder + ->replaceWith() + ->field('someField') + ->concat('$authorIp', 'foo'); + + self::assertEquals( + [ + '$replaceWith' => [ + 'someField' => ['$concat' => ['$ip', 'foo']], + ], + ], + $stage->getExpression(), + ); + } + + public function testFieldNameConversionWithDirectExpression(): void + { + $builder = $this->dm->createAggregationBuilder(CmsComment::class); + + $stage = $builder + ->replaceWith('$authorIp'); + + self::assertEquals( + ['$replaceWith' => '$ip'], + $stage->getExpression(), + ); + } +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SampleTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SampleTest.php index 376f94f05c..ef6bfc726a 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SampleTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SampleTest.php @@ -12,14 +12,14 @@ class SampleTest extends BaseTestCase { use AggregationTestTrait; - public function testSampleStage(): void + public function testStage(): void { $sampleStage = new Sample($this->getTestAggregationBuilder(), 10); self::assertSame(['$sample' => ['size' => 10]], $sampleStage->getExpression()); } - public function testSampleFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->sample(10); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SearchTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SearchTest.php new file mode 100644 index 0000000000..5903cfa2ef --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SearchTest.php @@ -0,0 +1,1274 @@ + [ + 'expectedOperator' => [ + 'autocomplete' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => 'content', + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->autocomplete('content', 'MongoDB', 'Aggregation', 'Pipeline') + ->path('content'); + }, + ]; + + yield 'Autocomplete with token order' => [ + 'expectedOperator' => [ + 'autocomplete' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => 'content', + 'tokenOrder' => 'any', + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->autocomplete() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->tokenOrder('any'); + }, + ]; + + yield 'Autocomplete with boost score' => [ + 'expectedOperator' => [ + 'autocomplete' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => 'content', + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->autocomplete() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->boostScore(1.5); + }, + ]; + + yield 'Autocomplete with constant score' => [ + 'expectedOperator' => [ + 'autocomplete' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => 'content', + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->autocomplete() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->constantScore(1.5); + }, + ]; + + yield 'Autocomplete with fuzzy search' => [ + 'expectedOperator' => [ + 'autocomplete' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => 'content', + 'fuzzy' => (object) [ + 'maxEdits' => 1, + 'prefixLength' => 2, + 'maxExpansions' => 3, + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->autocomplete() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->fuzzy(1, 2, 3); + }, + ]; + } + + public static function provideCompoundBuilders(): Generator + { + yield 'Compound with single must clause' => [ + 'expectedOperator' => [ + 'compound' => (object) [ + 'must' => [ + (object) [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['items.content'], + 'synonyms' => 'mySynonyms', + ], + ], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->compound() + ->must() + ->text() + ->path('items.content') + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->synonyms('mySynonyms'); + }, + ]; + + yield 'Compound with multiple must clauses' => [ + 'expectedOperator' => [ + 'compound' => (object) [ + 'must' => [ + (object) [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['items.content'], + 'synonyms' => 'mySynonyms', + ], + ], + (object) [ + 'near' => (object) [ + 'origin' => 5, + 'pivot' => 3, + 'path' => ['value1', 'value2'], + ], + ], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->compound() + ->must() + ->text() + ->path('items.content') + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->synonyms('mySynonyms') + ->near(5, 3, 'value1', 'value2'); + }, + ]; + + yield 'Compound with must and mustNot clauses' => [ + 'expectedOperator' => [ + 'compound' => (object) [ + 'must' => [ + (object) [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['items.content'], + 'synonyms' => 'mySynonyms', + ], + ], + ], + 'mustNot' => [ + (object) [ + 'exists' => (object) ['path' => 'hidden'], + ], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->compound() + ->must() + ->text() + ->path('items.content') + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->synonyms('mySynonyms') + ->mustNot() + ->exists('hidden'); + }, + ]; + } + + public static function provideEmbeddedDocumentBuilders(): Generator + { + yield 'EmbeddedDocument with single text operator' => [ + 'expectedOperator' => [ + 'embeddedDocument' => (object) [ + 'path' => 'items', + 'operator' => (object) [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['items.content'], + 'synonyms' => 'mySynonyms', + ], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->embeddedDocument('items') + ->text() + ->path('items.content') + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->synonyms('mySynonyms'); + }, + ]; + } + + public static function provideEmbeddedDocumentCompoundBuilders(): Generator + { + yield 'EmbeddedDocument with compound operator' => [ + 'expectedOperator' => [ + 'embeddedDocument' => (object) [ + 'path' => 'items', + 'operator' => (object) [ + 'compound' => (object) [ + 'must' => [ + (object) [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['items.content'], + 'synonyms' => 'mySynonyms', + ], + ], + (object) [ + 'near' => (object) [ + 'origin' => 5, + 'pivot' => 3, + 'path' => ['items.value1'], + ], + ], + ], + 'mustNot' => [ + (object) [ + 'exists' => (object) ['path' => 'items.hidden'], + ], + ], + ], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->embeddedDocument('items') + ->compound() + ->must() + ->text() + ->path('items.content') + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->synonyms('mySynonyms') + ->near(5, 3, 'items.value1') + ->mustNot() + ->exists('items.hidden'); + }, + ]; + } + + public static function provideEqualsBuilders(): Generator + { + yield 'Equals required only' => [ + 'expectedOperator' => [ + 'equals' => (object) [ + 'path' => 'content', + 'value' => 'MongoDB Aggregation Pipeline', + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->equals('content', 'MongoDB Aggregation Pipeline'); + }, + ]; + + yield 'Equals with boost score' => [ + 'expectedOperator' => [ + 'equals' => (object) [ + 'path' => 'content', + 'value' => 'MongoDB Aggregation Pipeline', + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->equals() + ->path('content') + ->value('MongoDB Aggregation Pipeline') + ->boostScore(1.5); + }, + ]; + + yield 'Equals with constant score' => [ + 'expectedOperator' => [ + 'equals' => (object) [ + 'path' => 'content', + 'value' => 'MongoDB Aggregation Pipeline', + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->equals() + ->path('content') + ->value('MongoDB Aggregation Pipeline') + ->constantScore(1.5); + }, + ]; + } + + public static function provideExistsBuilders(): Generator + { + yield 'Exists required only' => [ + 'expectedOperator' => [ + 'exists' => (object) ['path' => 'content'], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->exists('content'); + }, + ]; + } + + public static function provideGeoShapeBuilders(): Generator + { + yield 'CompoundedGeoShape required only' => [ + 'expectedOperator' => [ + 'geoShape' => (object) [ + 'path' => ['location1', 'location2'], + 'relation' => 'contains', + 'geometry' => ['coordinates' => [12.345, 23.456], 'type' => 'Point'], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->geoShape( + new Point([12.345, 23.456]), + 'contains', + 'location1', + 'location2', + ); + }, + ]; + + yield 'CompoundedGeoShape with boost score' => [ + 'expectedOperator' => [ + 'geoShape' => (object) [ + 'path' => ['location'], + 'relation' => 'contains', + 'geometry' => ['coordinates' => [12.345, 23.456], 'type' => 'Point'], + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->geoShape() + ->path('location') + ->relation('contains') + ->geometry(new Point([12.345, 23.456])) + ->boostScore(1.5); + }, + ]; + + yield 'CompoundedGeoShape with constant score' => [ + 'expectedOperator' => [ + 'geoShape' => (object) [ + 'path' => ['location'], + 'relation' => 'contains', + 'geometry' => ['coordinates' => [12.345, 23.456], 'type' => 'Point'], + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->geoShape() + ->path('location') + ->relation('contains') + ->geometry(new Point([12.345, 23.456])) + ->constantScore(1.5); + }, + ]; + } + + public static function provideGeoWithinBuilders(): Generator + { + yield 'GeoWithin box' => [ + 'expectedOperator' => [ + 'geoWithin' => (object) [ + 'path' => ['location1', 'location2'], + 'box' => (object) [ + 'bottomLeft' => ['coordinates' => [-12.345, -23.456], 'type' => 'Point'], + 'topRight' => ['coordinates' => [12.345, 23.456], 'type' => 'Point'], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->geoWithin('location1', 'location2') + ->box(new Point([-12.345, -23.456]), new Point([12.345, 23.456])); + }, + ]; + + yield 'GeoWithin circle' => [ + 'expectedOperator' => [ + 'geoWithin' => (object) [ + 'path' => ['location'], + 'circle' => (object) [ + 'center' => ['coordinates' => [12.345, 23.456], 'type' => 'Point'], + 'radius' => 3.14, + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->geoWithin() + ->path('location') + ->circle(new Point([12.345, 23.456]), 3.14); + }, + ]; + + yield 'GeoWithin geometry' => [ + 'expectedOperator' => [ + 'geoWithin' => (object) [ + 'path' => ['location'], + 'geometry' => [ + 'coordinates' => [ + [[0, 0], [0, 4], [4, 4], [4, 0], [0, 0]], + [[1, 1], [1, 3], [3, 3], [3, 1], [1, 1]], + ], + 'type' => 'Polygon', + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->geoWithin() + ->path('location') + ->geometry(new Polygon([ + [[0, 0], [0, 4], [4, 4], [4, 0], [0, 0]], + [[1, 1], [1, 3], [3, 3], [3, 1], [1, 1]], + ])); + }, + ]; + + yield 'GeoWithin with boost score' => [ + 'expectedOperator' => [ + 'geoWithin' => (object) [ + 'path' => ['location'], + 'circle' => (object) [ + 'center' => ['coordinates' => [12.345, 23.456], 'type' => 'Point'], + 'radius' => 3.14, + ], + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->geoWithin() + ->path('location') + ->circle(new Point([12.345, 23.456]), 3.14) + ->boostScore(1.5); + }, + ]; + + yield 'GeoWithin with constant score' => [ + 'expectedOperator' => [ + 'geoWithin' => (object) [ + 'path' => ['location'], + 'circle' => (object) [ + 'center' => ['coordinates' => [12.345, 23.456], 'type' => 'Point'], + 'radius' => 3.14, + ], + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->geoWithin() + ->path('location') + ->circle(new Point([12.345, 23.456]), 3.14) + ->constantScore(1.5); + }, + ]; + } + + public static function provideMoreLikeThisBuilders(): Generator + { + yield 'MoreLikeThis with single like' => [ + 'expectedOperator' => [ + 'moreLikeThis' => (object) ['like' => [['title' => 'The Godfather']]], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->moreLikeThis(['title' => 'The Godfather']); + }, + ]; + + yield 'MoreLikeThis with multiple documents' => [ + 'expectedOperator' => [ + 'moreLikeThis' => (object) [ + 'like' => [ + ['title' => 'The Godfather'], + ['title' => 'The Green Mile'], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->moreLikeThis(['title' => 'The Godfather'], ['title' => 'The Green Mile']); + }, + ]; + } + + public static function provideNearBuilders(): Generator + { + yield 'Near with number' => [ + 'expectedOperator' => [ + 'near' => (object) [ + 'origin' => 5, + 'pivot' => 3, + 'path' => ['value1', 'value2'], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->near(5, 3, 'value1', 'value2'); + }, + ]; + + $date = new UTCDateTime(); + + yield 'Near with date' => [ + 'expectedOperator' => [ + 'near' => (object) [ + 'origin' => $date, + 'pivot' => 2, + 'path' => ['createdAt'], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) use ($date) { + return $stage->near() + ->path('createdAt') + ->origin($date) + ->pivot(2); + }, + ]; + + yield 'Near with point' => [ + 'expectedOperator' => [ + 'near' => (object) [ + 'origin' => ['coordinates' => [12.345, 23.456], 'type' => 'Point'], + 'pivot' => 2, + 'path' => ['createdAt'], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->near() + ->path('createdAt') + ->origin(new Point([12.345, 23.456])) + ->pivot(2); + }, + ]; + } + + public static function providePhraseBuilders(): Generator + { + yield 'Phrase required only' => [ + 'expectedOperator' => [ + 'phrase' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->phrase() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content'); + }, + ]; + + yield 'Phrase with slop' => [ + 'expectedOperator' => [ + 'phrase' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['content'], + 'slop' => 3, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->phrase() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->slop(3); + }, + ]; + + yield 'Phrase with boost score' => [ + 'expectedOperator' => [ + 'phrase' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['content'], + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->phrase() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->boostScore(1.5); + }, + ]; + + yield 'Phrase with constant score' => [ + 'expectedOperator' => [ + 'phrase' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['content'], + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->phrase() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->constantScore(1.5); + }, + ]; + } + + public static function provideQueryStringBuilders(): Generator + { + yield 'QueryString required only' => [ + 'expectedOperator' => [ + 'queryString' => (object) [ + 'query' => 'MongoDB Aggregation Pipeline', + 'defaultPath' => 'content', + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->queryString('MongoDB Aggregation Pipeline', 'content'); + }, + ]; + + yield 'QueryString with boost score' => [ + 'expectedOperator' => [ + 'queryString' => (object) [ + 'query' => 'content:pipeline OR title:pipeline', + 'defaultPath' => 'content', + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->queryString() + ->query('content:pipeline OR title:pipeline') + ->defaultPath('content') + ->boostScore(1.5); + }, + ]; + + yield 'QueryString with constant score' => [ + 'expectedOperator' => [ + 'queryString' => (object) [ + 'query' => 'content:pipeline OR title:pipeline', + 'defaultPath' => 'content', + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->queryString() + ->query('content:pipeline OR title:pipeline') + ->defaultPath('content') + ->constantScore(1.5); + }, + ]; + } + + public static function provideRangeBuilders(): Generator + { + yield 'Range gt only' => [ + 'expectedOperator' => [ + 'range' => (object) [ + 'path' => ['field1', 'field2'], + 'gt' => 5, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->range() + ->path('field1', 'field2') + ->gt(5); + }, + ]; + + yield 'Range gte only' => [ + 'expectedOperator' => [ + 'range' => (object) [ + 'path' => ['field1', 'field2'], + 'gte' => 5, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->range() + ->path('field1', 'field2') + ->gte(5); + }, + ]; + + yield 'Range lt only' => [ + 'expectedOperator' => [ + 'range' => (object) [ + 'path' => ['field1', 'field2'], + 'lt' => 5, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->range() + ->path('field1', 'field2') + ->lt(5); + }, + ]; + + yield 'Range lte only' => [ + 'expectedOperator' => [ + 'range' => (object) [ + 'path' => ['field1', 'field2'], + 'lte' => 5, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->range() + ->path('field1', 'field2') + ->lte(5); + }, + ]; + + yield 'Range both bounds' => [ + 'expectedOperator' => [ + 'range' => (object) [ + 'path' => ['field1', 'field2'], + 'lte' => 10, + 'gte' => 5, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->range() + ->path('field1', 'field2') + ->lte(10) + ->gte(5); + }, + ]; + + yield 'Range with boost score' => [ + 'expectedOperator' => [ + 'range' => (object) [ + 'path' => ['field1', 'field2'], + 'lte' => 10, + 'gte' => 5, + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->range() + ->path('field1', 'field2') + ->lte(10) + ->gte(5) + ->boostScore(1.5); + }, + ]; + + yield 'Range with constant score' => [ + 'expectedOperator' => [ + 'range' => (object) [ + 'path' => ['field1', 'field2'], + 'lte' => 10, + 'gte' => 5, + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->range() + ->path('field1', 'field2') + ->lte(10) + ->gte(5) + ->constantScore(1.5); + }, + ]; + } + + public static function provideRegexBuilders(): Generator + { + yield 'Regex required only' => [ + 'expectedOperator' => [ + 'regex' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->regex() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content'); + }, + ]; + + yield 'Regex with allowAnalyzedField true' => [ + 'expectedOperator' => [ + 'regex' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + 'allowAnalyzedField' => true, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->regex() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content') + ->allowAnalyzedField(); + }, + ]; + + yield 'Regex with allowAnalyzedField false' => [ + 'expectedOperator' => [ + 'regex' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + 'allowAnalyzedField' => false, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->regex() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content') + ->allowAnalyzedField(false); + }, + ]; + + yield 'Regex with boost score' => [ + 'expectedOperator' => [ + 'regex' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->regex() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content') + ->boostScore(1.5); + }, + ]; + + yield 'Regex with constant score' => [ + 'expectedOperator' => [ + 'regex' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->regex() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content') + ->constantScore(1.5); + }, + ]; + } + + public static function provideTextBuilders(): Generator + { + yield 'Text required only' => [ + 'expectedOperator' => [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->text() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content'); + }, + ]; + + yield 'Text with synonyms' => [ + 'expectedOperator' => [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['content'], + 'synonyms' => 'mySynonyms', + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->text() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->synonyms('mySynonyms'); + }, + ]; + + yield 'Text with boost score' => [ + 'expectedOperator' => [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['content'], + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->text() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->boostScore(1.5); + }, + ]; + + yield 'Text with constant score' => [ + 'expectedOperator' => [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['content'], + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->text() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->constantScore(1.5); + }, + ]; + + yield 'Text with fuzzy search' => [ + 'expectedOperator' => [ + 'text' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['content'], + 'fuzzy' => (object) [ + 'maxEdits' => 1, + 'prefixLength' => 2, + 'maxExpansions' => 3, + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->text() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('content') + ->fuzzy(1, 2, 3); + }, + ]; + } + + public static function provideWildcardBuilders(): Generator + { + yield 'Wildcard required only' => [ + 'expectedOperator' => [ + 'wildcard' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->wildcard() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content'); + }, + ]; + + yield 'Wildcard with allowAnalyzedField true' => [ + 'expectedOperator' => [ + 'wildcard' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + 'allowAnalyzedField' => true, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->wildcard() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content') + ->allowAnalyzedField(); + }, + ]; + + yield 'Wildcard with allowAnalyzedField false' => [ + 'expectedOperator' => [ + 'wildcard' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + 'allowAnalyzedField' => false, + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->wildcard() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content') + ->allowAnalyzedField(false); + }, + ]; + + yield 'Wildcard with boost score' => [ + 'expectedOperator' => [ + 'wildcard' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + 'score' => (object) [ + 'boost' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->wildcard() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content') + ->boostScore(1.5); + }, + ]; + + yield 'Wildcard with constant score' => [ + 'expectedOperator' => [ + 'wildcard' => (object) [ + 'query' => ['MongoDB', 'Aggregation', 'Pipeline'], + 'path' => ['title', 'content'], + 'score' => (object) [ + 'constant' => (object) ['value' => 1.5], + ], + ], + ], + /** @param Search|CompoundSearchOperatorInterface $stage */ + 'createOperator' => static function ($stage) { + return $stage->wildcard() + ->query('MongoDB', 'Aggregation', 'Pipeline') + ->path('title', 'content') + ->constantScore(1.5); + }, + ]; + } + + /** + * @dataProvider provideAutocompleteBuilders + * @dataProvider provideCompoundBuilders + * @dataProvider provideEmbeddedDocumentBuilders + * @dataProvider provideEmbeddedDocumentCompoundBuilders + * @dataProvider provideEqualsBuilders + * @dataProvider provideExistsBuilders + * @dataProvider provideGeoShapeBuilders + * @dataProvider provideGeoWithinBuilders + * @dataProvider provideMoreLikeThisBuilders + * @dataProvider provideNearBuilders + * @dataProvider providePhraseBuilders + * @dataProvider provideQueryStringBuilders + * @dataProvider provideRangeBuilders + * @dataProvider provideRegexBuilders + * @dataProvider provideTextBuilders + * @dataProvider provideWildcardBuilders + */ + public function testSearchOperators(array $expectedOperator, Closure $createOperator): void + { + $baseExpected = [ + 'index' => 'my_search_index', + 'highlight' => (object) [ + 'path' => 'content', + 'maxCharsToExamine' => 2, + 'maxNumPassages' => 3, + ], + 'count' => (object) [ + 'type' => 'lowerBound', + 'threshold' => 1000, + ], + 'returnStoredSource' => true, + ]; + + $searchStage = new Search($this->getTestAggregationBuilder()); + $searchStage + ->index('my_search_index'); + + $result = $createOperator($searchStage); + + self::logicalOr( + new IsInstanceOf(AbstractSearchOperator::class), + new IsInstanceOf(Search::class), + ); + + $result + ->highlight('content', 2, 3) + ->countDocuments('lowerBound', 1000) + ->returnStoredSource(); + + self::assertEquals( + ['$search' => (object) array_merge($baseExpected, $expectedOperator)], + $searchStage->getExpression(), + ); + } + + /** + * @dataProvider provideAutocompleteBuilders + * @dataProvider provideEmbeddedDocumentBuilders + * @dataProvider provideEqualsBuilders + * @dataProvider provideExistsBuilders + * @dataProvider provideGeoShapeBuilders + * @dataProvider provideGeoWithinBuilders + * @dataProvider provideMoreLikeThisBuilders + * @dataProvider provideNearBuilders + * @dataProvider providePhraseBuilders + * @dataProvider provideQueryStringBuilders + * @dataProvider provideRangeBuilders + * @dataProvider provideRegexBuilders + * @dataProvider provideTextBuilders + * @dataProvider provideWildcardBuilders + */ + public function testSearchCompoundOperators(array $expectedOperator, Closure $createOperator): void + { + $searchStage = new Search($this->getTestAggregationBuilder()); + $compound = $searchStage + ->index('my_search_index') + ->compound(); + + $compound = $createOperator($compound->must()); + $compound = $createOperator($compound->mustNot()); + $compound = $createOperator($compound->should(2)); + $compound = $createOperator($compound->filter()); + + self::assertInstanceOf(CompoundSearchOperatorInterface::class, $compound); + + $keys = ['must', 'mustNot', 'should', 'filter']; + + $expected = (object) [ + 'index' => 'my_search_index', + 'compound' => (object) array_combine( + $keys, + array_map( + static fn (string $value): array => [(object) $expectedOperator], + $keys, + ), + ), + ]; + + $expected->compound->minimumShouldMatch = 2; + + self::assertEquals( + ['$search' => $expected], + $searchStage->getExpression(), + ); + } + + /** + * @dataProvider provideAutocompleteBuilders + * @dataProvider provideCompoundBuilders + * @dataProvider provideEqualsBuilders + * @dataProvider provideExistsBuilders + * @dataProvider provideGeoShapeBuilders + * @dataProvider provideGeoWithinBuilders + * @dataProvider provideMoreLikeThisBuilders + * @dataProvider provideNearBuilders + * @dataProvider providePhraseBuilders + * @dataProvider provideQueryStringBuilders + * @dataProvider provideRangeBuilders + * @dataProvider provideRegexBuilders + * @dataProvider provideTextBuilders + * @dataProvider provideWildcardBuilders + */ + public function testSearchEmbeddedDocumentOperators(array $expectedOperator, Closure $createOperator): void + { + $searchStage = new Search($this->getTestAggregationBuilder()); + $embedded = $searchStage + ->index('my_search_index') + ->embeddedDocument('foo'); + + $createOperator($embedded); + + $expected = (object) [ + 'index' => 'my_search_index', + 'embeddedDocument' => (object) [ + 'path' => 'foo', + 'operator' => (object) $expectedOperator, + ], + ]; + + self::assertEquals( + ['$search' => $expected], + $searchStage->getExpression(), + ); + } +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SetTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SetTest.php new file mode 100644 index 0000000000..337d772a73 --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SetTest.php @@ -0,0 +1,35 @@ +getTestAggregationBuilder()); + $setStage + ->field('product') + ->multiply('$field', 5); + + self::assertSame(['$set' => ['product' => ['$multiply' => ['$field', 5]]]], $setStage->getExpression()); + } + + public function testFromBuilder(): void + { + $builder = $this->getTestAggregationBuilder(); + $builder + ->set() + ->field('product') + ->multiply('$field', 5); + + self::assertSame([['$set' => ['product' => ['$multiply' => ['$field', 5]]]]], $builder->getPipeline()); + } +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SkipTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SkipTest.php index ef25526386..adba2c77f9 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SkipTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SkipTest.php @@ -12,14 +12,14 @@ class SkipTest extends BaseTestCase { use AggregationTestTrait; - public function testSkipStage(): void + public function testStage(): void { $skipStage = new Skip($this->getTestAggregationBuilder(), 10); self::assertSame(['$skip' => 10], $skipStage->getExpression()); } - public function testSkipFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->skip(10); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SortTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SortTest.php index 1032733018..2c11c88a97 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SortTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/SortTest.php @@ -19,7 +19,7 @@ class SortTest extends BaseTestCase * * @dataProvider provideSortOptions */ - public function testSortStage(array $expectedSort, $field, ?string $order = null): void + public function testStage(array $expectedSort, $field, ?string $order = null): void { $sortStage = new Sort($this->getTestAggregationBuilder(), $field, $order); @@ -32,7 +32,7 @@ public function testSortStage(array $expectedSort, $field, ?string $order = null * * @dataProvider provideSortOptions */ - public function testSortFromBuilder(array $expectedSort, $field, ?string $order = null): void + public function testFromBuilder(array $expectedSort, $field, ?string $order = null): void { $builder = $this->getTestAggregationBuilder(); $builder->sort($field, $order); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnionWithTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnionWithTest.php new file mode 100644 index 0000000000..d6645e5e3d --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnionWithTest.php @@ -0,0 +1,50 @@ +dm->createAggregationBuilder(SimpleReferenceUser::class); + $builder + ->unionWith(User::class); + + $expectedPipeline = [ + ['$unionWith' => (object) ['coll' => 'users']], + ]; + + self::assertEquals($expectedPipeline, $builder->getPipeline()); + } + + public function testStageWithCollectionName(): void + { + $unionBuilder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); + $unionBuilder->match() + ->field('foo')->equals('bar'); + + $builder = $this->dm->createAggregationBuilder(SimpleReferenceUser::class); + $builder + ->unionWith('someRandomCollectionName') + ->pipeline($unionBuilder); + + $expectedPipeline = [ + [ + '$unionWith' => (object) [ + 'coll' => 'someRandomCollectionName', + 'pipeline' => [ + ['$match' => ['foo' => 'bar']], + ], + ], + ], + ]; + + self::assertEquals($expectedPipeline, $builder->getPipeline()); + } +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnsetTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnsetTest.php new file mode 100644 index 0000000000..72d43800bf --- /dev/null +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnsetTest.php @@ -0,0 +1,31 @@ +dm->getUnitOfWork()->getDocumentPersister(User::class); + $unsetStage = new UnsetStage($this->getTestAggregationBuilder(), $documentPersister, 'id', 'foo', 'bar'); + + self::assertSame(['$unset' => ['_id', 'foo', 'bar']], $unsetStage->getExpression()); + } + + public function testFromBuilder(): void + { + $builder = $this->getTestAggregationBuilder(); + $builder->unset('id', 'foo', 'bar'); + + self::assertSame([['$unset' => ['_id', 'foo', 'bar']]], $builder->getPipeline()); + } +} diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnwindTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnwindTest.php index 1f8944b1ee..25dd807520 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnwindTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Aggregation/Stage/UnwindTest.php @@ -12,14 +12,14 @@ class UnwindTest extends BaseTestCase { use AggregationTestTrait; - public function testUnwindStage(): void + public function testStage(): void { $unwindStage = new Unwind($this->getTestAggregationBuilder(), 'fieldName'); self::assertSame(['$unwind' => 'fieldName'], $unwindStage->getExpression()); } - public function testUnwindStageWithNewFields(): void + public function testStageWithNewFields(): void { $unwindStage = new Unwind($this->getTestAggregationBuilder(), 'fieldName'); $unwindStage @@ -29,7 +29,7 @@ public function testUnwindStageWithNewFields(): void self::assertSame(['$unwind' => ['path' => 'fieldName', 'includeArrayIndex' => 'index', 'preserveNullAndEmptyArrays' => true]], $unwindStage->getExpression()); } - public function testUnwindFromBuilder(): void + public function testFromBuilder(): void { $builder = $this->getTestAggregationBuilder(); $builder->unwind('fieldName'); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php b/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php index a6866191e5..cac71da05d 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/DocumentManagerTest.php @@ -35,8 +35,6 @@ use RuntimeException; use stdClass; -use function get_class; - class DocumentManagerTest extends BaseTestCase { public function testCustomRepository(): void @@ -193,7 +191,7 @@ public function testDisriminatedSimpleReferenceFails(): void $d = new WrongSimpleRefDocument(); $r = new ParticipantSolo('Maciej'); $this->dm->persist($r); - $class = $this->dm->getClassMetadata(get_class($d)); + $class = $this->dm->getClassMetadata($d::class); $this->expectException(MappingException::class); $this->expectExceptionMessage( 'Identifier reference must not target document using Single Collection Inheritance, ' . @@ -207,7 +205,7 @@ public function testDifferentStoreAsDbReferences(): void $r = new User(); $this->dm->persist($r); $d = new ReferenceStoreAsDocument(); - $class = $this->dm->getClassMetadata(get_class($d)); + $class = $this->dm->getClassMetadata($d::class); $dbRef = $this->dm->createReference($r, $class->associationMappings['ref1']); self::assertInstanceOf(ObjectId::class, $dbRef); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php index b03d45670e..96856cf30d 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Events/LifecycleListenersTest.php @@ -15,8 +15,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use PHPUnit\Framework\Assert; -use function get_class; - class LifecycleListenersTest extends BaseTestCase { private MyEventListener $listener; @@ -175,8 +173,8 @@ public function testChangeToReferenceFieldTriggersEvents(): void Events::postUpdate => [TestDocument::class], ]; - $document = $dm->getRepository(get_class($document))->find($document->id); - $profile = $dm->getRepository(get_class($profile))->find($profile->id); + $document = $dm->getRepository($document::class)->find($document->id); + $profile = $dm->getRepository($profile::class)->find($profile->id); $this->listener->called = []; $document->profile = $profile; $dm->flush(); @@ -184,8 +182,8 @@ public function testChangeToReferenceFieldTriggersEvents(): void self::assertEquals($called, $this->listener->called, 'Changing ReferenceOne field did not dispatched proper events.'); $this->listener->called = []; - $document = $dm->getRepository(get_class($document))->find($document->id); - $profile = $dm->getRepository(get_class($profile))->find($profile->id); + $document = $dm->getRepository($document::class)->find($document->id); + $profile = $dm->getRepository($profile::class)->find($profile->id); $this->listener->called = []; $document->profiles[] = $profile; $dm->flush(); @@ -204,7 +202,7 @@ public function testPostCollectionLoad(): void $this->dm->flush(); $this->dm->clear(); - $document = $this->dm->getRepository(get_class($document))->find($document->id); + $document = $this->dm->getRepository($document::class)->find($document->id); self::assertInstanceOf(PersistentCollectionInterface::class, $document->embedded); $document->embedded->add(new TestEmbeddedDocument('For mock at 1')); // mock at 0, despite adding postCollectionLoad will have empty collection @@ -212,7 +210,7 @@ public function testPostCollectionLoad(): void $this->dm->flush(); $this->dm->clear(); - $document = $this->dm->getRepository(get_class($document))->find($document->id); + $document = $this->dm->getRepository($document::class)->find($document->id); self::assertInstanceOf(PersistentCollectionInterface::class, $document->embedded); $document->embedded->add(new TestEmbeddedDocument('Will not be seen')); // mock at 1, collection should have 1 element after @@ -229,7 +227,7 @@ class MyEventListener public function __call(string $method, array $args): void { $document = $args[0]->getDocument(); - $className = get_class($document); + $className = $document::class; $this->called[$method][] = $className; } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/AtomicSetTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/AtomicSetTest.php index 2447e3b51b..a9469214cb 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/AtomicSetTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/AtomicSetTest.php @@ -17,8 +17,6 @@ use Documents\Phonenumber; use MongoDB\BSON\ObjectId; -use function get_class; - /** * CollectionPersister will throw exception when collection with atomicSet * or atomicSetArray should be handled by it. If no exception was thrown it @@ -52,7 +50,7 @@ public function testAtomicInsertAndUpdate(): void self::assertCount(1, $this->logger, 'Inserting a document with an embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertCount(1, $user->phonenumbers); self::assertEquals('12345678', $user->phonenumbers[0]->getPhonenumber()); @@ -64,7 +62,7 @@ public function testAtomicInsertAndUpdate(): void self::assertCount(1, $this->logger, 'Updating a document and its embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertEquals('Malarz', $user->surname); self::assertCount(2, $user->phonenumbers); @@ -82,7 +80,7 @@ public function testAtomicUpsert(): void self::assertCount(1, $this->logger, 'Upserting a document with an embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertCount(1, $user->phonenumbers); self::assertEquals('12345678', $user->phonenumbers[0]->getPhonenumber()); @@ -102,7 +100,7 @@ public function testAtomicCollectionUnset($clearWith): void self::assertCount(1, $this->logger, 'Inserting a document with an embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertCount(1, $user->phonenumbers); self::assertEquals('12345678', $user->phonenumbers[0]->getPhonenumber()); @@ -114,7 +112,7 @@ public function testAtomicCollectionUnset($clearWith): void self::assertCount(1, $this->logger, 'Updating a document and unsetting its embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertEquals('Malarz', $user->surname); self::assertEmpty($user->phonenumbers); @@ -138,7 +136,7 @@ public function testAtomicCollectionClearAndUpdate(): void self::assertCount(1, $this->logger, 'Inserting a document with an embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); $user->phonenumbers->clear(); $user->phonenumbers[] = new Phonenumber('87654321'); $this->logger->clear(); @@ -146,7 +144,7 @@ public function testAtomicCollectionClearAndUpdate(): void self::assertCount(1, $this->logger, 'Updating emptied collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertCount(1, $user->phonenumbers); self::assertEquals('87654321', $user->phonenumbers[0]->getPhonenumber()); @@ -161,14 +159,14 @@ public function testAtomicCollectionReplacedAndUpdated(): void self::assertCount(1, $this->logger, 'Inserting a document with an embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); $user->phonenumbers = new ArrayCollection([new Phonenumber('87654321')]); $this->logger->clear(); $this->dm->flush(); self::assertCount(1, $this->logger, 'Updating emptied collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertCount(1, $user->phonenumbers); self::assertEquals('87654321', $user->phonenumbers[0]->getPhonenumber()); @@ -184,7 +182,7 @@ public function testAtomicSetArray(): void self::assertCount(1, $this->logger, 'Inserting a document with an embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertCount(2, $user->phonenumbersArray); self::assertEquals('12345678', $user->phonenumbersArray[0]->getPhonenumber()); @@ -196,7 +194,7 @@ public function testAtomicSetArray(): void self::assertCount(1, $this->logger, 'Unsetting an element within an embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertCount(1, $user->phonenumbersArray); self::assertEquals('87654321', $user->phonenumbersArray[0]->getPhonenumber()); self::assertFalse(isset($user->phonenumbersArray[1])); @@ -213,7 +211,7 @@ public function testAtomicCollectionWithAnotherNested(): void self::assertCount(1, $this->logger, 'Inserting a document with a nested embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertCount(1, $user->phonebooks); $privateBook = $user->phonebooks[0]; @@ -230,7 +228,7 @@ public function testAtomicCollectionWithAnotherNested(): void self::assertCount(1, $this->logger, 'Updating multiple, nested embed-many collections requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertEquals('Maciej', $user->name); self::assertCount(2, $user->phonebooks); $privateBook = $user->phonebooks[0]; @@ -248,7 +246,7 @@ public function testAtomicCollectionWithAnotherNested(): void self::assertCount(1, $this->logger, 'Clearing a nested embed-many collection requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertCount(2, $user->phonebooks); $privateBook = $user->phonebooks[0]; self::assertEquals('Private', $privateBook->getTitle()); @@ -273,7 +271,7 @@ public function testWeNeedToGoDeeper(): void self::assertCount(1, $this->logger, 'Inserting a document with nested embed-many collections requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertCount(1, $user->inception); self::assertEquals('start', $user->inception[0]->value); self::assertNotNull($user->inception[0]->one); @@ -296,7 +294,7 @@ public function testWeNeedToGoDeeper(): void self::assertCount(1, $this->logger, 'Updating nested collections on various levels requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertCount(1, $user->inception); self::assertEquals('start', $user->inception[0]->value); self::assertNotNull($user->inception[0]->one); @@ -331,7 +329,7 @@ public function testUpdatingNestedCollectionWhileDeletingParent(): void self::assertCount(1, $this->logger, 'Inserting a document with nested embed-many collections requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertCount(1, $user->inception); self::assertEquals('start', $user->inception[0]->value); self::assertCount(2, $user->inception[0]->many); @@ -353,7 +351,7 @@ public function testUpdatingNestedCollectionWhileDeletingParent(): void self::assertCount(1, $this->logger, 'Updating nested collections while deleting parents requires one query'); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->find($user->id); + $user = $this->dm->getRepository($user::class)->find($user->id); self::assertCount(1, $user->inception); self::assertEquals('start', $user->inception[0]->value); self::assertCount(2, $user->inception[0]->many); @@ -381,18 +379,18 @@ public function testAtomicRefMany(): void self::assertCount(1, $this->logger, 'Updating empty atomic reference many requires one query'); $this->dm->clear(); - $malarzm = $this->dm->find(get_class($malarzm), $malarzm->id); + $malarzm = $this->dm->find($malarzm::class, $malarzm->id); self::assertCount(1, $malarzm->friends); self::assertEquals('Jeremy', $malarzm->friends[0]->name); - $jonwage = $this->dm->find(get_class($jonwage), $jonwage->id); + $jonwage = $this->dm->find($jonwage::class, $jonwage->id); $malarzm->friends[] = $jonwage; $this->logger->clear(); $this->dm->flush(); self::assertCount(1, $this->logger, 'Updating existing atomic reference many requires one query'); $this->dm->clear(); - $malarzm = $this->dm->find(get_class($malarzm), $malarzm->id); + $malarzm = $this->dm->find($malarzm::class, $malarzm->id); self::assertCount(2, $malarzm->friends); self::assertEquals('Jeremy', $malarzm->friends[0]->name); self::assertEquals('Jon', $malarzm->friends[1]->name); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/BinDataTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/BinDataTest.php index 4533e4b25c..e302606c4e 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/BinDataTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/BinDataTest.php @@ -8,8 +8,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use MongoDB\BSON\Binary; -use function get_class; - class BinDataTest extends BaseTestCase { /** @dataProvider provideData */ @@ -20,7 +18,7 @@ public function testBinData(string $field, string $data, int $type): void $this->dm->persist($test); $this->dm->flush(); - $check = $this->dm->getDocumentCollection(get_class($test))->findOne([]); + $check = $this->dm->getDocumentCollection($test::class)->findOne([]); self::assertInstanceOf(Binary::class, $check[$field]); self::assertEquals($type, $check[$field]->getType()); self::assertEquals($data, $check[$field]->getData()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionPersisterTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionPersisterTest.php index b5e0fb73d6..58933d24dd 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionPersisterTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/CollectionPersisterTest.php @@ -13,8 +13,6 @@ use Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class CollectionPersisterTest extends BaseTestCase { private CommandLogger $logger; @@ -363,8 +361,8 @@ public function testFindBySetStrategyKey(): void $this->dm->persist($post); $this->dm->flush(); - self::assertSame($post, $this->dm->getRepository(get_class($post))->findOneBy(['comments.a.by' => 'userA'])); - self::assertSame($post, $this->dm->getRepository(get_class($post))->findOneBy(['comments.a.comments.b.by' => 'userB'])); + self::assertSame($post, $this->dm->getRepository($post::class)->findOneBy(['comments.a.by' => 'userA'])); + self::assertSame($post, $this->dm->getRepository($post::class)->findOneBy(['comments.a.comments.b.by' => 'userB'])); } public function testPersistSeveralNestedEmbedManySetStrategy(): void @@ -395,7 +393,7 @@ public function testPersistSeveralNestedEmbedManySetStrategy(): void 'Modification of embedded-many collections of one document by "set" strategy requires one query', ); - self::assertSame($structure, $this->dm->getRepository(get_class($structure))->findOneBy(['id' => $structure->id])); + self::assertSame($structure, $this->dm->getRepository($structure::class)->findOneBy(['id' => $structure->id])); } public function testPersistSeveralNestedEmbedManySetArrayStrategy(): void @@ -426,7 +424,7 @@ public function testPersistSeveralNestedEmbedManySetArrayStrategy(): void 'Modification of embedded-many collections of one document by "setArray" strategy requires one query', ); - self::assertSame($structure, $this->dm->getRepository(get_class($structure))->findOneBy(['id' => $structure->id])); + self::assertSame($structure, $this->dm->getRepository($structure::class)->findOneBy(['id' => $structure->id])); } public function testPersistSeveralNestedEmbedManyAddToSetStrategy(): void @@ -457,7 +455,7 @@ public function testPersistSeveralNestedEmbedManyAddToSetStrategy(): void 'Modification of embedded-many collections of one document by "addToSet" strategy requires two queries', ); - self::assertSame($structure, $this->dm->getRepository(get_class($structure))->findOneBy(['id' => $structure->id])); + self::assertSame($structure, $this->dm->getRepository($structure::class)->findOneBy(['id' => $structure->id])); } public function testPersistSeveralNestedEmbedManyPushAllStrategy(): void @@ -488,7 +486,7 @@ public function testPersistSeveralNestedEmbedManyPushAllStrategy(): void 'Modification of embedded-many collections of one document by "pushAll" strategy requires two queries', ); - self::assertSame($structure, $this->dm->getRepository(get_class($structure))->findOneBy(['id' => $structure->id])); + self::assertSame($structure, $this->dm->getRepository($structure::class)->findOneBy(['id' => $structure->id])); } public function testPersistSeveralNestedEmbedManyDifferentStrategies(): void @@ -522,7 +520,7 @@ public function testPersistSeveralNestedEmbedManyDifferentStrategies(): void 'Modification of embedded-many collections of one document by "set", "setArray" and "pushAll" strategies requires two queries', ); - self::assertSame($structure, $this->dm->getRepository(get_class($structure))->findOneBy(['id' => $structure->id])); + self::assertSame($structure, $this->dm->getRepository($structure::class)->findOneBy(['id' => $structure->id])); } public function testPersistSeveralNestedEmbedManyDifferentStrategiesDeepNesting(): void @@ -562,7 +560,7 @@ public function testPersistSeveralNestedEmbedManyDifferentStrategiesDeepNesting( 'Modification of embedded-many collections of one document by "set", "setArray" and "pushAll" strategies requires two queries', ); - self::assertSame($structure, $this->dm->getRepository(get_class($structure))->findOneBy(['id' => $structure->id])); + self::assertSame($structure, $this->dm->getRepository($structure::class)->findOneBy(['id' => $structure->id])); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/CommitImprovementTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/CommitImprovementTest.php index 5ecb7894e8..a16571c702 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/CommitImprovementTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/CommitImprovementTest.php @@ -16,8 +16,6 @@ use Documents\User; use Documents\VersionedUser; -use function get_class; - class CommitImprovementTest extends BaseTestCase { private CommandLogger $logger; @@ -49,7 +47,7 @@ public function testInsertIncludesAllNestedCollections(): void self::assertCount(1, $this->logger, 'Inserting a document includes all nested collections and requires one query'); $this->dm->clear(); - $user = $this->dm->find(get_class($user), $user->getId()); + $user = $this->dm->find($user::class, $user->getId()); self::assertEquals('malarzm', $user->getUsername()); self::assertCount(1, $user->getPhonebooks()); self::assertEquals('Private', $user->getPhonebooks()->first()->getTitle()); @@ -75,11 +73,11 @@ public function testCollectionsAreUpdatedJustAfterOwningDocument(): void $troll->setVersion(3); try { $this->dm->flush(); - } catch (LockException $ex) { + } catch (LockException) { } $this->dm->clear(); - $user = $this->dm->find(get_class($user), $user->getId()); + $user = $this->dm->find($user::class, $user->getId()); $phonenumbers = $user->getPhonebooks()->first()->getPhonenumbers(); self::assertCount(2, $phonenumbers); self::assertEquals('12345678', $phonenumbers[0]->getPhonenumber()); @@ -106,7 +104,7 @@ public function testChangingCollectionInPostEventsHasNoIllEffects(): void self::assertInstanceOf(PersistentCollectionInterface::class, $phoneNumbers); // so we got a number on postPersist self::assertTrue($phoneNumbers->isDirty()); // but they should be dirty - $collection = $this->dm->getDocumentCollection(get_class($user)); + $collection = $this->dm->getDocumentCollection($user::class); $inDb = $collection->findOne(); self::assertArrayNotHasKey('phonenumbers', $inDb, 'Collection modified in postPersist should not be in database without recomputing change set'); @@ -129,19 +127,19 @@ public function testSchedulingCollectionDeletionAfterSchedulingForUpdate(): void $this->dm->flush(); $user->addPhonenumber(new Phonenumber('87654321')); - $this->uow->computeChangeSet($this->dm->getClassMetadata(get_class($user)), $user); + $this->uow->computeChangeSet($this->dm->getClassMetadata($user::class), $user); self::assertTrue($this->uow->isCollectionScheduledForUpdate($user->getPhonenumbers())); self::assertFalse($this->uow->isCollectionScheduledForDeletion($user->getPhonenumbers())); $user->getPhonenumbers()->clear(); - $this->uow->computeChangeSet($this->dm->getClassMetadata(get_class($user)), $user); + $this->uow->computeChangeSet($this->dm->getClassMetadata($user::class), $user); self::assertFalse($this->uow->isCollectionScheduledForUpdate($user->getPhonenumbers())); self::assertTrue($this->uow->isCollectionScheduledForDeletion($user->getPhonenumbers())); $this->logger->clear(); $this->dm->flush(); $this->dm->clear(); - $user = $this->dm->find(get_class($user), $user->getId()); + $user = $this->dm->find($user::class, $user->getId()); self::assertEmpty($user->getPhonenumbers()); } } @@ -182,7 +180,7 @@ public function __call(string $eventName, array $args): void // prove that even this won't break our flow $dm = $args[0]->getDocumentManager(); $dm->getUnitOfWork()->recomputeSingleDocumentChangeSet( - $dm->getClassMetadata(get_class($document)), + $dm->getClassMetadata($document::class), $document, ); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomCollectionsTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomCollectionsTest.php index 6cebd2417d..5e2d6b73ea 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomCollectionsTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/CustomCollectionsTest.php @@ -19,21 +19,20 @@ use stdClass; use function assert; -use function get_class; class CustomCollectionsTest extends BaseTestCase { public function testMappingNamespaceIsAdded(): void { $d = new DocumentWithCustomCollection(); - $cm = $this->dm->getClassMetadata(get_class($d)); + $cm = $this->dm->getClassMetadata($d::class); self::assertSame(MyEmbedsCollection::class, $cm->fieldMappings['coll']['collectionClass']); } public function testLeadingBackslashIsRemoved(): void { $d = new DocumentWithCustomCollection(); - $cm = $this->dm->getClassMetadata(get_class($d)); + $cm = $this->dm->getClassMetadata($d::class); self::assertSame(MyDocumentsCollection::class, $cm->fieldMappings['inverseRefMany']['collectionClass']); } @@ -82,7 +81,7 @@ public function testEmbedMany(): void self::assertInstanceOf(PersistentCollection::class, $d->boring); $this->dm->clear(); - $d = $this->dm->find(get_class($d), $d->id); + $d = $this->dm->find($d::class, $d->id); self::assertNotInstanceOf(PersistentCollection::class, $d->coll); self::assertInstanceOf(MyEmbedsCollection::class, $d->coll); @@ -107,7 +106,7 @@ public function testReferenceMany(): void $this->dm->flush(); $this->dm->clear(); - $d2 = $this->dm->find(get_class($d2), $d2->id); + $d2 = $this->dm->find($d2::class, $d2->id); self::assertNotInstanceOf(PersistentCollection::class, $d2->refMany); self::assertInstanceOf(MyDocumentsCollection::class, $d2->refMany); self::assertCount(2, $d2->refMany); @@ -125,7 +124,7 @@ public function testInverseSideOfReferenceMany(): void $this->dm->flush(); $this->dm->clear(); - $d2 = $this->dm->find(get_class($d2), $d2->id); + $d2 = $this->dm->find($d2::class, $d2->id); self::assertNotInstanceOf(PersistentCollection::class, $d2->inverseRefMany); self::assertInstanceOf(MyDocumentsCollection::class, $d2->inverseRefMany); } @@ -138,12 +137,12 @@ public function testModifyingCollectionByCustomMethod(): void $this->dm->persist($d); $this->dm->flush(); - $d = $this->dm->find(get_class($d), $d->id); + $d = $this->dm->find($d::class, $d->id); $d->coll->move(0, 1); $this->dm->flush(); $this->dm->clear(); - $d = $this->dm->find(get_class($d), $d->id); + $d = $this->dm->find($d::class, $d->id); self::assertCount(2, $d->coll); self::assertEquals($e2, $d->coll[0]); self::assertEquals($e1, $d->coll[1]); @@ -163,12 +162,12 @@ public function testModifyingCollectionInChangeTrackingNotifyDocument(): void $this->dm->persist($profile); $this->dm->flush(); - $profile = $this->dm->find(get_class($profile), $profile->getProfileId()); + $profile = $this->dm->find($profile::class, $profile->getProfileId()); $profile->getImages()->move(0, 1); $this->dm->flush(); $this->dm->clear(); - $profile = $this->dm->find(get_class($profile), $profile->getProfileId()); + $profile = $this->dm->find($profile::class, $profile->getProfileId()); self::assertCount(2, $profile->getImages()); self::assertEquals($f2->getId(), $profile->getImages()[0]->getId()); self::assertEquals($f1->getId(), $profile->getImages()[1]->getId()); @@ -180,7 +179,7 @@ public function testMethodWithVoidReturnType(): void $this->dm->persist($d); $this->dm->flush(); - $d = $this->dm->find(get_class($d), $d->id); + $d = $this->dm->find($d::class, $d->id); self::assertInstanceOf(MyEmbedsCollection::class, $d->coll); $d->coll->nothingReally(); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php index 12752e3bea..9ff46e92d8 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DateTest.php @@ -10,7 +10,6 @@ use InvalidArgumentException; use MongoDB\BSON\UTCDateTime; -use function get_class; use function time; use const PHP_INT_SIZE; @@ -51,7 +50,7 @@ public function testDateInstanceChangeDoesNotCauseUpdateIfValueIsTheSame($oldVal $this->dm->flush(); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->findOneBy([]); + $user = $this->dm->getRepository($user::class)->findOneBy([]); $user->setCreatedAt($newValue); $this->dm->getUnitOfWork()->computeChangeSets(); $changeset = $this->dm->getUnitOfWork()->getDocumentChangeSet($user); @@ -78,9 +77,10 @@ public function testDateInstanceValueChangeDoesCauseUpdateIfValueIsTheSame(): vo $this->dm->flush(); $this->dm->clear(); - $user = $this->dm->getRepository(get_class($user))->findOneBy([]); - self::assertInstanceOf(DateTime::class, $user->getCreatedAt()); - $user->getCreatedAt()->setTimestamp(time() - 3600); + $user = $this->dm->getRepository($user::class)->findOneBy([]); + $createdAt = $user->getCreatedAt(); + self::assertInstanceOf(DateTime::class, $createdAt); + $createdAt->setTimestamp(time() - 3600); $this->dm->getUnitOfWork()->computeChangeSets(); $changeset = $this->dm->getUnitOfWork()->getDocumentChangeSet($user); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php index 24f318f3df..5cccaf761c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/DocumentPersisterTest.php @@ -22,9 +22,7 @@ use MongoDB\Driver\WriteConcern; use ReflectionProperty; -use function get_class; -use function gettype; -use function is_object; +use function get_debug_type; use function sprintf; class DocumentPersisterTest extends BaseTestCase @@ -1082,7 +1080,7 @@ private static function createException($value): InvalidArgumentException 'Expected "%s" or "%s", got "%s"', DocumentPersisterCustomTypedId::class, ObjectId::class, - is_object($value) ? get_class($value) : gettype($value), + get_debug_type($value), ), ); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php index e7e8e1ffd5..e27c082821 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/EmbeddedTest.php @@ -23,7 +23,6 @@ use MongoDB\BSON\ObjectId; use function assert; -use function get_class; class EmbeddedTest extends BaseTestCase { @@ -192,7 +191,7 @@ public function testPostRemoveEventOnEmbeddedManyDocument(): void $this->dm->clear(); // retrieve test - $test = $this->dm->createQueryBuilder(get_class($test)) + $test = $this->dm->createQueryBuilder($test::class) ->field('id')->equals($test->id) ->getQuery() ->getSingleResult(); @@ -228,7 +227,7 @@ public function testRemoveEmbeddedManyDocument(): void $this->dm->clear(); // retrieve test - $test = $this->dm->createQueryBuilder(get_class($test)) + $test = $this->dm->createQueryBuilder($test::class) ->field('id')->equals($test->id) ->getQuery() ->getSingleResult(); @@ -246,7 +245,7 @@ public function testRemoveEmbeddedManyDocument(): void self::assertEquals(0, $test->level1->count()); // retrieve test - $test = $this->dm->createQueryBuilder(get_class($test)) + $test = $this->dm->createQueryBuilder($test::class) ->field('id')->equals($test->id) ->getQuery() ->getSingleResult(); @@ -281,7 +280,7 @@ public function testRemoveDeepEmbeddedManyDocument(): void $this->dm->clear(); // retrieve test - $test = $this->dm->createQueryBuilder(get_class($test)) + $test = $this->dm->createQueryBuilder($test::class) ->field('id')->equals($test->id) ->getQuery() ->getSingleResult(); @@ -301,7 +300,7 @@ public function testRemoveDeepEmbeddedManyDocument(): void self::assertEquals(0, $level1->level2->count()); // retrieve test - $test = $this->dm->createQueryBuilder(get_class($test)) + $test = $this->dm->createQueryBuilder($test::class) ->field('id')->equals($test->id) ->getQuery() ->getSingleResult(); @@ -334,7 +333,7 @@ public function testPostRemoveEventOnDeepEmbeddedManyDocument(): void $this->dm->clear(); // retrieve test - $test = $this->dm->createQueryBuilder(get_class($test)) + $test = $this->dm->createQueryBuilder($test::class) ->field('id')->equals($test->id) ->getQuery() ->getSingleResult(); @@ -372,7 +371,7 @@ public function testEmbeddedLoadEvents(): void $this->dm->flush(); $this->dm->clear(); - $test = $this->dm->createQueryBuilder(get_class($test)) + $test = $this->dm->createQueryBuilder($test::class) ->field('id')->equals($test->id) ->getQuery() ->getSingleResult(); @@ -504,7 +503,7 @@ public function testChangeEmbedOneDocumentId(): void $this->dm->flush(); $this->dm->clear(); - $test = $this->dm->find(get_class($test), $test->id); + $test = $this->dm->find($test::class, $test->id); self::assertEquals($newId, $test->embed->id); } @@ -527,7 +526,7 @@ public function testChangeEmbedManyDocumentId(): void $this->dm->flush(); $this->dm->clear(); - $test = $this->dm->find(get_class($test), $test->id); + $test = $this->dm->find($test::class, $test->id); self::assertEquals($newId, $test->embedMany[0]->id); } @@ -545,7 +544,7 @@ public function testEmbeddedDocumentsWithSameIdAreNotSameInstance(): void $this->dm->flush(); $this->dm->clear(); - $test = $this->dm->find(get_class($test), $test->id); + $test = $this->dm->find($test::class, $test->id); self::assertNotSame($test->embed, $test->embedMany[0]); self::assertNotSame($test->embed, $test->embedMany[1]); @@ -577,7 +576,7 @@ public function testWhenCopyingManyEmbedSubDocumentsFromOneDocumentToAnotherWill $this->dm->flush(); $this->dm->clear(); //get clean results from mongo - $test1 = $this->dm->find(get_class($test1), $test1->id); + $test1 = $this->dm->find($test1::class, $test1->id); self::assertCount(1, $test1->embedMany); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilterTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilterTest.php index 2d255ca381..69f61000e8 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilterTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/FilterTest.php @@ -212,7 +212,7 @@ protected function getGroupsByReference(): array foreach ($tim->getGroups() as $group) { try { $groupnames[] = $group->getName(); - } catch (DocumentNotFoundException $e) { + } catch (DocumentNotFoundException) { //Proxy object filtered } } @@ -242,7 +242,7 @@ protected function getProfileByReference(): ?string $profile = $tim->getProfile(); try { return $profile->getFirstname(); - } catch (DocumentNotFoundException $e) { + } catch (DocumentNotFoundException) { //Proxy object filtered return null; } @@ -270,13 +270,13 @@ protected function getUsernamesWithDocumentManager(): array try { $usernames[] = $tim->getUsername(); - } catch (DocumentNotFoundException $e) { + } catch (DocumentNotFoundException) { //Proxy object filtered } try { $usernames[] = $john->getUsername(); - } catch (DocumentNotFoundException $e) { + } catch (DocumentNotFoundException) { //Proxy object filtered } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushTest.php index 48028505c4..3ca2fcb347 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/FlushTest.php @@ -7,8 +7,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\FriendUser; -use function get_class; - class FlushTest extends BaseTestCase { /** @@ -41,7 +39,7 @@ public function testFlush(): void $this->assertSize(0); - $userA = $this->dm->find(get_class($userA), $userA->id); + $userA = $this->dm->find($userA::class, $userA->id); // the size is 1. userA is in the uow. $this->assertSize(1); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdTest.php index fd5b811c68..268b9b3097 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdTest.php @@ -228,7 +228,7 @@ public function testIdTypesAndStrategies(string $type, string $strategy, $id = n self::assertNotNull($object->id); if ($expectedMongoType !== null) { - $check = $this->dm->getDocumentCollection(get_class($object))->findOne([]); + $check = $this->dm->getDocumentCollection($object::class)->findOne([]); self::assertEquals($expectedMongoType, is_object($check['_id']) ? get_class($check['_id']) : gettype($check['_id'])); } @@ -236,7 +236,7 @@ public function testIdTypesAndStrategies(string $type, string $strategy, $id = n self::assertEquals($expected, $object->id); } - $object = $this->dm->find(get_class($object), $object->id); + $object = $this->dm->find($object::class, $object->id); self::assertNotNull($object); if ($expected !== null) { @@ -247,7 +247,7 @@ public function testIdTypesAndStrategies(string $type, string $strategy, $id = n $this->dm->flush(); $this->dm->clear(); - $object = $this->dm->find(get_class($object), $object->id); + $object = $this->dm->find($object::class, $object->id); self::assertEquals('changed', $object->test); } @@ -320,7 +320,7 @@ public function testBinIds(string $type, int $expectedMongoBinDataType, string $ $this->dm->flush(); $this->dm->clear(); - $check = $this->dm->getDocumentCollection(get_class($object))->findOne([]); + $check = $this->dm->getDocumentCollection($object::class)->findOne([]); self::assertEquals(Binary::class, get_class($check['_id'])); self::assertEquals($expectedMongoBinDataType, $check['_id']->getType()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php index 3bd928a372..9a0cadb921 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/IdentifiersTest.php @@ -26,7 +26,7 @@ public function testGetIdentifierValue(): void $this->dm->flush(); $this->dm->clear(); - $test = $this->dm->getRepository(get_class($event))->find($event->getId()); + $test = $this->dm->getRepository($event::class)->find($event->getId()); $userTest = $test->getUser(); self::assertEquals($user->getId(), $userTest->getId()); @@ -37,14 +37,16 @@ public function testGetIdentifierValue(): void $class = $this->dm->getClassMetadata(get_class($test->getUser())); - $test = $this->dm->getRepository(get_class($event))->find($event->getId()); - self::assertEquals($user->getId(), $class->getIdentifierValue($test->getUser())); - self::assertEquals($user->getId(), $class->getFieldValue($test->getUser(), 'id')); - self::assertInstanceOf(LazyLoadingInterface::class, $test->getUser()); - self::assertFalse($test->getUser()->isProxyInitialized()); + $test = $this->dm->getRepository($event::class)->find($event->getId()); - self::assertEquals('jwage', $test->getUser()->getUsername()); - self::assertTrue($test->getUser()->isProxyInitialized()); + $foundUser = $test->getUser(); + self::assertEquals($user->getId(), $class->getIdentifierValue($user)); + self::assertEquals($user->getId(), $class->getFieldValue($foundUser, 'id')); + self::assertInstanceOf(LazyLoadingInterface::class, $foundUser); + self::assertFalse($foundUser->isProxyInitialized()); + + self::assertEquals('jwage', $foundUser->getUsername()); + self::assertTrue($foundUser->isProxyInitialized()); } public function testIdentifiersAreSet(): void diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php index b4985d7287..7771290965 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/LockTest.php @@ -21,7 +21,6 @@ use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; -use function get_class; use function time; class LockTest extends BaseTestCase @@ -66,7 +65,7 @@ public function testOptimisticLockingIntThrowsException(): void $this->dm->flush(); // Manually change the version so the next code will cause an exception - $this->dm->getDocumentCollection(get_class($article))->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => 5]]); + $this->dm->getDocumentCollection($article::class)->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => 5]]); // Now lets change a property and try and save it again $article->title = 'ok'; @@ -241,7 +240,7 @@ public function testLockDateThrowsException(): void $this->dm->flush(); // Manually change the version so the next code will cause an exception - $this->dm->getDocumentCollection(get_class($article))->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => new UTCDateTime(time() * 1000 + 600)]]); + $this->dm->getDocumentCollection($article::class)->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => new UTCDateTime(time() * 1000 + 600)]]); // Now lets change a property and try and save it again $article->title = 'ok'; @@ -258,7 +257,7 @@ public function testLockDateImmutableThrowsException(): void $this->dm->flush(); // Manually change the version so the next code will cause an exception - $this->dm->getDocumentCollection(get_class($article))->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => new UTCDateTime(time() * 1000 + 600)]]); + $this->dm->getDocumentCollection($article::class)->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => new UTCDateTime(time() * 1000 + 600)]]); // Now lets change a property and try and save it again $article->title = 'ok'; @@ -275,7 +274,7 @@ public function testLockDecimal128ThrowsException(): void $this->dm->flush(); // Manually change the version so the next code will cause an exception - $this->dm->getDocumentCollection(get_class($article))->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => new Decimal128('3')]]); + $this->dm->getDocumentCollection($article::class)->updateOne(['_id' => new ObjectId($article->id)], ['$set' => ['version' => new Decimal128('3')]]); // Now lets change a property and try and save it again $article->title = 'ok'; @@ -344,7 +343,7 @@ public function testLockPessimisticWrite(): void $this->dm->lock($article, LockMode::PESSIMISTIC_WRITE); - $check = $this->dm->getDocumentCollection(get_class($article))->findOne(); + $check = $this->dm->getDocumentCollection($article::class)->findOne(); self::assertEquals(LockMode::PESSIMISTIC_WRITE, $check['locked']); } @@ -358,7 +357,7 @@ public function testLockPessimisticRead(): void $this->dm->lock($article, LockMode::PESSIMISTIC_READ); - $check = $this->dm->getDocumentCollection(get_class($article))->findOne(); + $check = $this->dm->getDocumentCollection($article::class)->findOne(); self::assertEquals(LockMode::PESSIMISTIC_READ, $check['locked']); } @@ -372,13 +371,13 @@ public function testUnlock(): void $this->dm->lock($article, LockMode::PESSIMISTIC_READ); - $check = $this->dm->getDocumentCollection(get_class($article))->findOne(); + $check = $this->dm->getDocumentCollection($article::class)->findOne(); self::assertEquals(LockMode::PESSIMISTIC_READ, $check['locked']); self::assertEquals(LockMode::PESSIMISTIC_READ, $article->locked); $this->dm->unlock($article); - $check = $this->dm->getDocumentCollection(get_class($article))->findOne(); + $check = $this->dm->getDocumentCollection($article::class)->findOne(); self::assertArrayNotHasKey('locked', $check); self::assertNull($article->locked); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedCollectionsTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedCollectionsTest.php index 4028b0ecb7..b4d9ff80d0 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedCollectionsTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/NestedCollectionsTest.php @@ -11,8 +11,6 @@ use Documents\Phonebook; use Documents\Phonenumber; -use function get_class; - class NestedCollectionsTest extends BaseTestCase { /** @dataProvider provideStrategy */ @@ -26,7 +24,7 @@ public function testStrategy(string $field): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->getRepository(get_class($doc))->find($doc->id); + $doc = $this->dm->getRepository($doc::class)->find($doc->id); self::assertCount(1, $doc->{$field}); $privateBook = $doc->{$field}[0]; self::assertEquals('Private', $privateBook->getTitle()); @@ -40,7 +38,7 @@ public function testStrategy(string $field): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->getRepository(get_class($doc))->find($doc->id); + $doc = $this->dm->getRepository($doc::class)->find($doc->id); self::assertCount(2, $doc->{$field}); $privateBook = $doc->{$field}[0]; self::assertEquals('Private', $privateBook->getTitle()); @@ -55,7 +53,7 @@ public function testStrategy(string $field): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->getRepository(get_class($doc))->find($doc->id); + $doc = $this->dm->getRepository($doc::class)->find($doc->id); self::assertCount(2, $doc->{$field}); $privateBook = $doc->{$field}[0]; self::assertEquals('Private', $privateBook->getTitle()); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/OwningAndInverseReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/OwningAndInverseReferencesTest.php index 65d8d681b4..8d10d61d13 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/OwningAndInverseReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/OwningAndInverseReferencesTest.php @@ -17,7 +17,6 @@ use Documents\Tag; use function assert; -use function get_class; use function strtotime; class OwningAndInverseReferencesTest extends BaseTestCase @@ -47,7 +46,7 @@ public function testOneToOne(): void self::assertInstanceOf(Cart::class, $customer->cart); self::assertEquals($customer->cart->id, $customer->cart->id); - $check = $this->dm->getDocumentCollection(get_class($customer))->findOne(); + $check = $this->dm->getDocumentCollection($customer::class)->findOne(); self::assertArrayHasKey('cartTest', $check); self::assertEquals('test', $check['cartTest']); @@ -56,7 +55,7 @@ public function testOneToOne(): void $this->dm->flush(); $this->dm->clear(); - $check = $this->dm->getDocumentCollection(get_class($customer))->findOne(); + $check = $this->dm->getDocumentCollection($customer::class)->findOne(); self::assertArrayHasKey('cartTest', $check); self::assertEquals('ok', $check['cartTest']); @@ -74,13 +73,13 @@ public function testOneToManyBiDirectional(): void $this->dm->flush(); $this->dm->clear(); - $check = $this->dm->getDocumentCollection(get_class($product))->findOne(); + $check = $this->dm->getDocumentCollection($product::class)->findOne(); self::assertArrayNotHasKey('tags', $check); $check = $this->dm->getDocumentCollection(Feature::class)->findOne(); self::assertArrayHasKey('product', $check); - $product = $this->dm->createQueryBuilder(get_class($product)) + $product = $this->dm->createQueryBuilder($product::class) ->getQuery() ->getSingleResult(); assert($product instanceof Product); @@ -100,11 +99,11 @@ public function testOneToManySelfReferencing(): void $this->dm->flush(); $this->dm->clear(); - $check = $this->dm->getDocumentCollection(get_class($node))->findOne(['parent' => ['$exists' => false]]); + $check = $this->dm->getDocumentCollection($node::class)->findOne(['parent' => ['$exists' => false]]); self::assertNotNull($check); self::assertArrayNotHasKey('children', $check); - $root = $this->dm->createQueryBuilder(get_class($node)) + $root = $this->dm->createQueryBuilder($node::class) ->field('children')->exists(false) ->getQuery() ->getSingleResult(); @@ -132,7 +131,7 @@ public function testManyToMany(): void $this->dm->flush(); $this->dm->clear(); - $check = $this->dm->getDocumentCollection(get_class($blogPost))->findOne(); + $check = $this->dm->getDocumentCollection($blogPost::class)->findOne(); self::assertCount(1, $check['tags']); $check = $this->dm->getDocumentCollection(Tag::class)->findOne(); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistentCollectionCloneTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistentCollectionCloneTest.php index 16dbc59233..c619c0f6c2 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistentCollectionCloneTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/PersistentCollectionCloneTest.php @@ -9,8 +9,6 @@ use Documents\CmsGroup; use Documents\CmsUser; -use function get_class; - class PersistentCollectionCloneTest extends BaseTestCase { private ?CmsUser $user1 = null; @@ -43,8 +41,8 @@ public function setUp(): void $this->dm->flush(); $this->dm->clear(); - $this->user1 = $this->dm->find(get_class($user1), $user1->id); - $this->user2 = $this->dm->find(get_class($user1), $user2->id); + $this->user1 = $this->dm->find($user1::class, $user1->id); + $this->user2 = $this->dm->find($user1::class, $user2->id); } public function testClonePersistentCollectionAndReuse(): void @@ -56,7 +54,7 @@ public function testClonePersistentCollectionAndReuse(): void $this->dm->flush(); $this->dm->clear(); - $user1 = $this->dm->find(get_class($user1), $user1->id); + $user1 = $this->dm->find($user1::class, $user1->id); self::assertCount(2, $user1->groups); } @@ -71,8 +69,8 @@ public function testClonePersistentCollectionAndShare(): void $this->dm->flush(); $this->dm->clear(); - $user1 = $this->dm->find(get_class($user1), $user1->id); - $user2 = $this->dm->find(get_class($user1), $user2->id); + $user1 = $this->dm->find($user1::class, $user1->id); + $user2 = $this->dm->find($user1::class, $user2->id); self::assertCount(2, $user1->groups); self::assertCount(2, $user2->groups); @@ -92,8 +90,8 @@ public function testCloneThenDirtyPersistentCollection(): void $this->dm->flush(); $this->dm->clear(); - $user1 = $this->dm->find(get_class($user1), $user1->id); - $user2 = $this->dm->find(get_class($user1), $user2->id); + $user1 = $this->dm->find($user1::class, $user1->id); + $user2 = $this->dm->find($user1::class, $user2->id); self::assertCount(3, $user2->groups); self::assertCount(2, $user1->groups); @@ -116,8 +114,8 @@ public function testNotCloneAndPassAroundFlush(): void $this->dm->flush(); $this->dm->clear(); - $user1 = $this->dm->find(get_class($user1), $user1->id); - $user2 = $this->dm->find(get_class($user1), $user2->id); + $user1 = $this->dm->find($user1::class, $user1->id); + $user2 = $this->dm->find($user1::class, $user2->id); self::assertCount(3, $user2->groups); self::assertCount(3, $user1->groups); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryTest.php index 01c57abb2c..0bdaec1cf9 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/QueryTest.php @@ -21,7 +21,6 @@ use function array_values; use function assert; -use function get_class; use function iterator_to_array; use function strtotime; @@ -174,7 +173,7 @@ public function testDistinctWithDifferentDbName(): void $this->dm->flush(); $this->dm->clear(); - $results = $this->dm->createQueryBuilder(get_class($c1)) + $results = $this->dm->createQueryBuilder($c1::class) ->distinct('authorIp') ->getQuery() ->execute(); @@ -380,7 +379,7 @@ public function testQueryIsIterable(): void $query = $qb->getQuery(); self::assertInstanceOf(IteratorAggregate::class, $query); foreach ($query as $article) { - self::assertEquals(Article::class, get_class($article)); + self::assertEquals(Article::class, $article::class); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/RawTypeTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/RawTypeTest.php index fd159e7425..df97dbb373 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/RawTypeTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/RawTypeTest.php @@ -9,8 +9,6 @@ use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; -use function get_class; - class RawTypeTest extends BaseTestCase { /** @@ -26,7 +24,7 @@ public function testRawType($value): void $this->dm->persist($test); $this->dm->flush(); - $result = $this->dm->getDocumentCollection(get_class($test))->findOne(['_id' => new ObjectId($test->id)]); + $result = $this->dm->getDocumentCollection($test::class)->findOne(['_id' => new ObjectId($test->id)]); self::assertEquals($value, $result['raw']); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php index c9e9e20eb3..64bf494f5c 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ReferencesTest.php @@ -26,7 +26,6 @@ use ProxyManager\Proxy\LazyLoadingInterface; use function assert; -use function get_class; class ReferencesTest extends BaseTestCase { @@ -103,15 +102,16 @@ public function testLazyLoadedWithNotifyPropertyChanged(): void $this->dm->flush(); $this->dm->clear(); - $user = $this->dm->find(get_class($user), $user->getId()); - self::assertInstanceOf(GhostObjectInterface::class, $user->getProfileNotify()); - self::assertFalse($user->getProfileNotify()->isProxyInitialized()); + $user = $this->dm->find($user::class, $user->getId()); + $profile = $user->getProfileNotify(); + self::assertInstanceOf(GhostObjectInterface::class, $profile); + self::assertFalse($profile->isProxyInitialized()); $user->getProfileNotify()->setLastName('Malarz'); $this->dm->flush(); $this->dm->clear(); - $profile = $this->dm->find(get_class($profile), $profile->getProfileId()); + $profile = $this->dm->find($profile::class, $profile->getProfileId()); self::assertEquals('Maciej', $profile->getFirstName()); self::assertEquals('Malarz', $profile->getLastName()); } @@ -358,7 +358,7 @@ public function testSortReferenceManyOwningSide(): void $this->dm->flush(); $this->dm->clear(); - $user = $this->dm->find(get_class($user), $user->getId()); + $user = $this->dm->find($user::class, $user->getId()); $groups = $user->getSortedAscGroups(); self::assertEquals(2, $groups->count()); @@ -384,7 +384,7 @@ public function testDocumentNotFoundExceptionWithArrayId(): void $this->dm->flush(); $this->dm->clear(); - $collection = $this->dm->getDocumentCollection(get_class($test)); + $collection = $this->dm->getDocumentCollection($test::class); $collection->updateOne( ['_id' => new ObjectId($test->id)], @@ -395,7 +395,7 @@ public function testDocumentNotFoundExceptionWithArrayId(): void ], ); - $test = $this->dm->find(get_class($test), $test->id); + $test = $this->dm->find($test::class, $test->id); self::assertInstanceOf(LazyLoadingInterface::class, $test->referenceOne); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( @@ -416,7 +416,7 @@ public function testDocumentNotFoundExceptionWithObjectId(): void $this->dm->flush(); $this->dm->clear(); - $collection = $this->dm->getDocumentCollection(get_class($user)); + $collection = $this->dm->getDocumentCollection($user::class); $invalidId = new ObjectId('abcdefabcdefabcdefabcdef'); @@ -427,7 +427,7 @@ public function testDocumentNotFoundExceptionWithObjectId(): void ], ); - $user = $this->dm->find(get_class($user), $user->getId()); + $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfile(); self::assertInstanceOf(LazyLoadingInterface::class, $profile); $this->expectException(DocumentNotFoundException::class); @@ -448,7 +448,7 @@ public function testDocumentNotFoundExceptionWithMongoBinDataId(): void $this->dm->flush(); $this->dm->clear(); - $collection = $this->dm->getDocumentCollection(get_class($test)); + $collection = $this->dm->getDocumentCollection($test::class); $invalidBinData = new Binary('testbindata', Binary::TYPE_OLD_BINARY); @@ -459,7 +459,7 @@ public function testDocumentNotFoundExceptionWithMongoBinDataId(): void ], ); - $test = $this->dm->find(get_class($test), $test->id); + $test = $this->dm->find($test::class, $test->id); self::assertInstanceOf(LazyLoadingInterface::class, $test->referenceOne); $this->expectException(DocumentNotFoundException::class); $this->expectExceptionMessage( @@ -480,7 +480,7 @@ public function testDocumentNotFoundEvent(): void $this->dm->flush(); $this->dm->clear(); - $collection = $this->dm->getDocumentCollection(get_class($user)); + $collection = $this->dm->getDocumentCollection($user::class); $invalidId = new ObjectId('abcdefabcdefabcdefabcdef'); @@ -491,7 +491,7 @@ public function testDocumentNotFoundEvent(): void ], ); - $user = $this->dm->find(get_class($user), $user->getId()); + $user = $this->dm->find($user::class, $user->getId()); $profile = $user->getProfile(); $closure = static function (DocumentNotFoundEventArgs $eventArgs) use ($profile) { @@ -568,11 +568,8 @@ class DocumentWithMongoBinDataId class DocumentNotFoundListener { - private Closure $closure; - - public function __construct(Closure $closure) + public function __construct(private Closure $closure) { - $this->closure = $closure; } public function documentNotFound(DocumentNotFoundEventArgs $eventArgs): void diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ShardKeyTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ShardKeyTest.php index b2c17c69cc..ab5494361f 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/ShardKeyTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/ShardKeyTest.php @@ -12,7 +12,6 @@ use function assert; use function end; -use function get_class; /** @group sharding */ class ShardKeyTest extends BaseTestCase @@ -45,7 +44,7 @@ public function testUpdateAfterSave(): void $this->dm->persist($o); $this->dm->flush(); - $o = $this->dm->find(get_class($o), $o->id); + $o = $this->dm->find($o::class, $o->id); assert($o instanceof ShardedOne); $o->title = 'test2'; $this->dm->flush(); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SplObjectHashCollisionsTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SplObjectHashCollisionsTest.php index 600fb7907a..49ac2c935e 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/SplObjectHashCollisionsTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/SplObjectHashCollisionsTest.php @@ -10,8 +10,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use ReflectionObject; -use function get_class; - class SplObjectHashCollisionsTest extends BaseTestCase { /** @@ -67,7 +65,7 @@ static function (DocumentManager $dm): void { ], [ static function (DocumentManager $dm, $doc): void { - $dm->clear(get_class($doc)); + $dm->clear($doc::class); }, 1, ], diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1058Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1058Test.php index d29ce80ea9..e030d816f1 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1058Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1058Test.php @@ -12,7 +12,6 @@ use MongoDB\BSON\ObjectId; use function array_merge; -use function get_class; class GH1058Test extends BaseTestCase { @@ -47,7 +46,7 @@ public function onFlush(OnFlushEventArgs $args): void foreach (array_merge($uow->getScheduledDocumentInsertions(), $uow->getScheduledDocumentUpserts()) as $document) { $document->setValue('value 2'); - $metadata = $dm->getClassMetadata(get_class($document)); + $metadata = $dm->getClassMetadata($document::class); $dm->getUnitOfWork()->recomputeSingleDocumentChangeSet($metadata, $document); if ($uow->isScheduledForUpdate($document)) { diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1117Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1117Test.php index c0e59b8589..3d68d12949 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1117Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1117Test.php @@ -9,8 +9,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class GH1117Test extends BaseTestCase { public function testAddOnUninitializedCollection(): void @@ -21,12 +19,12 @@ public function testAddOnUninitializedCollection(): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->getRepository(get_class($doc))->find($doc->id); + $doc = $this->dm->getRepository($doc::class)->find($doc->id); $doc->embeds->add(new GH1117EmbeddedDocument('two')); $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->getRepository(get_class($doc))->find($doc->id); + $doc = $this->dm->getRepository($doc::class)->find($doc->id); self::assertCount(2, $doc->embeds); self::assertEquals('one', $doc->embeds[0]->value); self::assertEquals('two', $doc->embeds[1]->value); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1132Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1132Test.php index 232b87e994..0221d62506 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1132Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1132Test.php @@ -8,8 +8,6 @@ use Documents\Phonenumber; use Documents\User; -use function get_class; - class GH1132Test extends BaseTestCase { public function testClonedPersistentCollectionCanBeClearedAndUsedInNewDocument(): void @@ -26,10 +24,10 @@ public function testClonedPersistentCollectionCanBeClearedAndUsedInNewDocument() $this->dm->flush(); $this->dm->clear(); - $u = $this->dm->find(get_class($u), $u->getId()); + $u = $this->dm->find($u::class, $u->getId()); self::assertCount(1, $u->getPhonenumbers()); - $u2 = $this->dm->find(get_class($u2), $u2->getId()); + $u2 = $this->dm->find($u2::class, $u2->getId()); self::assertEmpty($u2->getPhonenumbers()); } @@ -48,10 +46,10 @@ public function testClonedPersistentCollectionCanBeClearedAndUsedInManagedDocume $this->dm->flush(); $this->dm->clear(); - $u = $this->dm->find(get_class($u), $u->getId()); + $u = $this->dm->find($u::class, $u->getId()); self::assertCount(1, $u->getPhonenumbers()); - $u2 = $this->dm->find(get_class($u2), $u2->getId()); + $u2 = $this->dm->find($u2::class, $u2->getId()); self::assertEmpty($u2->getPhonenumbers()); } @@ -71,10 +69,10 @@ public function testClonedPersistentCollectionUpdatesCorrectly(): void $this->dm->flush(); $this->dm->clear(); - $u = $this->dm->find(get_class($u), $u->getId()); + $u = $this->dm->find($u::class, $u->getId()); self::assertCount(1, $u->getPhonenumbers()); - $u2 = $this->dm->find(get_class($u2), $u2->getId()); + $u2 = $this->dm->find($u2::class, $u2->getId()); self::assertCount(1, $u2->getPhonenumbers()); self::assertSame('123456', $u2->getPhonenumbers()->first()->getPhonenumber()); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1225Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1225Test.php index 60e9d321e9..007df73fe2 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1225Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH1225Test.php @@ -9,8 +9,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class GH1225Test extends BaseTestCase { public function testRemoveAddEmbeddedDocToExistingDocumentWithPreUpdateHook(): void @@ -21,14 +19,14 @@ public function testRemoveAddEmbeddedDocToExistingDocumentWithPreUpdateHook(): v $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->getRepository(get_class($doc))->find($doc->id); + $doc = $this->dm->getRepository($doc::class)->find($doc->id); $embeddedDoc = $doc->embeds->first(); $doc->embeds->clear(); $doc->embeds->add($embeddedDoc); $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->getRepository(get_class($doc))->find($doc->id); + $doc = $this->dm->getRepository($doc::class)->find($doc->id); self::assertCount(1, $doc->embeds); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2002Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2002Test.php index da621e1cbd..21b080383f 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2002Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH2002Test.php @@ -8,7 +8,6 @@ use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; use function sprintf; class GH2002Test extends BaseTestCase @@ -22,7 +21,7 @@ public function testBuildingReferenceCreatesCorrectStructure(array $expectedRefe { $this->dm->persist($document); - $metadata = $this->dm->getClassMetadata(get_class($document)); + $metadata = $this->dm->getClassMetadata($document::class); $this->dm->getUnitOfWork()->computeChangeSet($metadata, $document); $data = $this->dm->getUnitOfWork()->getPersistenceBuilder()->prepareInsertData($document); @@ -65,7 +64,7 @@ public function testBuildingReferenceForUnlistedClassCausesException(string $exp { $this->dm->persist($document); - $metadata = $this->dm->getClassMetadata(get_class($document)); + $metadata = $this->dm->getClassMetadata($document::class); $this->dm->getUnitOfWork()->computeChangeSet($metadata, $document); $this->expectException(MappingException::class); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH245Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH245Test.php index 7babe582d5..84c59b2c67 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH245Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH245Test.php @@ -7,8 +7,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class GH245Test extends BaseTestCase { public function testTest(): void @@ -24,11 +22,11 @@ public function testTest(): void $this->dm->flush(); $this->dm->clear(); - $user = $this->dm->find(get_class($order), $order->id); + $user = $this->dm->find($order::class, $order->id); self::assertIsInt($order->id); - $check = $this->dm->getDocumentCollection(get_class($orderLog))->findOne(); + $check = $this->dm->getDocumentCollection($orderLog::class)->findOne(); self::assertIsInt($check['order']['$id']); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH453Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH453Test.php index 5037441237..bf11f081af 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH453Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH453Test.php @@ -10,7 +10,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use function array_values; -use function get_class; use function sprintf; class GH453Test extends BaseTestCase @@ -29,7 +28,7 @@ public function testHashWithStringKeys(): void $this->assertBsonObjectAndValue($hash, $doc->id, 'hash'); // Check that the value is hydrated properly - $doc = $this->dm->find(get_class($doc), $doc->id); + $doc = $this->dm->find($doc::class, $doc->id); self::assertSame($hash, $doc->hash); @@ -60,7 +59,7 @@ public function testHashWithNumericKeys(): void $this->assertBsonObjectAndValue($hash, $doc->id, 'hash'); // Check that the value is hydrated properly - $doc = $this->dm->find(get_class($doc), $doc->id); + $doc = $this->dm->find($doc::class, $doc->id); self::assertSame($hash, $doc->hash); @@ -93,7 +92,7 @@ public function testCollection(): void $this->assertBsonArrayAndValue($col, $doc->id, 'colSet'); // Check that the value is hydrated properly - $doc = $this->dm->find(get_class($doc), $doc->id); + $doc = $this->dm->find($doc::class, $doc->id); self::assertSame($col, $doc->colPush); self::assertSame($col, $doc->colSet); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH529Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH529Test.php index ca0897491a..ee2c3cb46e 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH529Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH529Test.php @@ -8,8 +8,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use MongoDB\BSON\ObjectId; -use function get_class; - class GH529Test extends BaseTestCase { public function testAutoIdWithConsistentValues(): void @@ -22,7 +20,7 @@ public function testAutoIdWithConsistentValues(): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->find(get_class($doc), $doc->id); + $doc = $this->dm->find($doc::class, $doc->id); self::assertNotNull($doc); self::assertEquals($identifier, $doc->id); @@ -40,7 +38,7 @@ public function testCustomIdType(): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->find(get_class($doc), $doc->id); + $doc = $this->dm->find($doc::class, $doc->id); self::assertNotNull($doc); self::assertSame('foo', $doc->id); @@ -55,7 +53,7 @@ public function testIntIdWithConsistentValues(): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->find(get_class($doc), $doc->id); + $doc = $this->dm->find($doc::class, $doc->id); self::assertNotNull($doc); self::assertSame(1, $doc->id); @@ -70,7 +68,7 @@ public function testIntIdWithInconsistentValues(): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->find(get_class($doc), $doc->id); + $doc = $this->dm->find($doc::class, $doc->id); self::assertNotNull($doc); self::assertNotEquals(3.14, $doc->id); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH580Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH580Test.php index d24171cc13..ca7230b523 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH580Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH580Test.php @@ -37,7 +37,7 @@ public function testDocumentPersisterShouldClearQueuedInsertsOnMongoException(): try { $this->dm->flush(); $this->fail('Expected BulkWriteException for duplicate value'); - } catch (BulkWriteException $e) { + } catch (BulkWriteException) { } $this->dm->clear($class); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH683Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH683Test.php index ad03a9bf9d..58571e0d19 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH683Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH683Test.php @@ -10,8 +10,6 @@ use Documents\Functional\Ticket\GH683\EmbeddedSubDocument2; use Documents\Functional\Ticket\GH683\ParentDocument; -use function get_class; - class GH683Test extends BaseTestCase { public function testEmbedOne(): void @@ -30,8 +28,8 @@ public function testEmbedOne(): void $id = $parent->id; - $parent = $this->dm->find(get_class($parent), $id); - self::assertInstanceOf(get_class($sub1), $parent->embedOne); + $parent = $this->dm->find($parent::class, $id); + self::assertInstanceOf($sub1::class, $parent->embedOne); } public function testEmbedMany(): void @@ -55,10 +53,10 @@ public function testEmbedMany(): void $id = $parent->id; - $parent = $this->dm->find(get_class($parent), $id); + $parent = $this->dm->find($parent::class, $id); $firstSub = $parent->embedMany->get(0); $secondSub = $parent->embedMany->get(1); - self::assertInstanceOf(get_class($sub1), $firstSub); - self::assertInstanceOf(get_class($sub2), $secondSub); + self::assertInstanceOf($sub1::class, $firstSub); + self::assertInstanceOf($sub2::class, $secondSub); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH774Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH774Test.php index 5f977577d5..8d1836d558 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH774Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH774Test.php @@ -9,8 +9,6 @@ use Doctrine\Persistence\Mapping\Driver\MappingDriver; use MongoDB\BSON\ObjectId; -use function get_class; - class GH774Test extends BaseTestCase { public function testUpsert(): void @@ -25,7 +23,7 @@ public function testUpsert(): void $this->dm->flush(); $this->dm->clear(); - $thread = $this->dm->find(get_class($thread), $id); + $thread = $this->dm->find($thread::class, $id); self::assertNotNull($thread); self::assertEquals('test', $thread->permalink); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH788Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH788Test.php index 2221df55dd..a5bef32ed8 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH788Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH788Test.php @@ -10,8 +10,6 @@ use Doctrine\ODM\MongoDB\Mapping\MappingException; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class GH788Test extends BaseTestCase { public function testDocumentWithDiscriminatorMap(): void @@ -26,7 +24,7 @@ public function testDocumentWithDiscriminatorMap(): void $this->dm->flush(); $this->dm->clear(); - $doc = $this->dm->find(get_class($listed), $listed->id); + $doc = $this->dm->find($listed::class, $listed->id); self::assertInstanceOf(GH788DocumentListed::class, $doc); self::assertEquals('listed', $doc->name); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php index 53249b2be4..a582fbc5f1 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH852Test.php @@ -14,8 +14,6 @@ use MongoDB\BSON\Binary; use ProxyManager\Proxy\GhostObjectInterface; -use function get_class; - class GH852Test extends BaseTestCase { /** @dataProvider provideIdGenerators */ @@ -45,7 +43,7 @@ public function testA(Closure $idGenerator): void $this->dm->flush(); $this->dm->clear(); - $parent = $this->dm->find(get_class($parent), $idGenerator('parent')); + $parent = $this->dm->find($parent::class, $idGenerator('parent')); self::assertNotNull($parent); self::assertEquals($idGenerator('parent'), $parent->id); self::assertEquals('parent', $parent->name); @@ -76,7 +74,7 @@ public function testA(Closure $idGenerator): void // these lines are relevant for $useKeys = false in ReferencePrimer::__construct() $this->dm->clear(); - $docs = $this->dm->createQueryBuilder(get_class($parent)) + $docs = $this->dm->createQueryBuilder($parent::class) ->field('name')->equals('parent') ->field('refMany')->prime() ->getQuery()->execute(); @@ -85,13 +83,13 @@ public function testA(Closure $idGenerator): void self::assertCount(2, $docs->current()->refMany); $this->dm->clear(); - $docs = $this->dm->createQueryBuilder(get_class($parent)) + $docs = $this->dm->createQueryBuilder($parent::class) ->getQuery()->execute(); self::assertCount(4, $docs); // these lines are relevant for $useKeys = false in DocumentRepository::matching() $this->dm->clear(); - $docs = $this->dm->getRepository(get_class($parent)) + $docs = $this->dm->getRepository($parent::class) ->matching(new Criteria()); self::assertCount(4, $docs); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH897Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH897Test.php index 063ada455a..3952b090f0 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH897Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH897Test.php @@ -8,8 +8,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class GH897Test extends BaseTestCase { public function testRecomputeSingleDocumentChangesetForManagedDocumentWithoutChangeset(): void @@ -97,7 +95,7 @@ public function preFlush(): void $documentA = $this->refOne; $documentA->name .= '-changed'; - $class = $this->dm->getClassMetadata(get_class($documentA)); + $class = $this->dm->getClassMetadata($documentA::class); $this->dm->getUnitOfWork()->recomputeSingleDocumentChangeSet($class, $documentA); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH944Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH944Test.php index fc6f85b17e..5711ea757b 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH944Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH944Test.php @@ -9,8 +9,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class GH944Test extends BaseTestCase { public function testIssue(): void @@ -22,20 +20,20 @@ public function testIssue(): void $this->dm->flush(); $this->dm->clear(); - $d = $this->dm->find(get_class($d), $d->id); + $d = $this->dm->find($d::class, $d->id); self::assertCount(2, $d->data); $d->removeByText('1'); self::assertCount(1, $d->data); $this->dm->flush(); - $d = $this->dm->find(get_class($d), $d->id); + $d = $this->dm->find($d::class, $d->id); self::assertCount(1, $d->data); $d->removeByText('2'); self::assertEmpty($d->data); $this->dm->flush(); $this->dm->clear(); - $d = $this->dm->find(get_class($d), $d->id); + $d = $this->dm->find($d::class, $d->id); self::assertEmpty($d->data); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH977Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH977Test.php index d5f0312121..10527c969a 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH977Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH977Test.php @@ -7,8 +7,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class GH977Test extends BaseTestCase { public function testAutoRecompute(): void @@ -19,9 +17,9 @@ public function testAutoRecompute(): void $this->dm->flush(); $this->dm->clear(); - $d = $this->dm->getRepository(get_class($d))->findOneBy(['value1' => 'Value 1']); + $d = $this->dm->getRepository($d::class)->findOneBy(['value1' => 'Value 1']); $d->value1 = 'Changed'; - $this->uow->computeChangeSet($this->dm->getClassMetadata(get_class($d)), $d); + $this->uow->computeChangeSet($this->dm->getClassMetadata($d::class), $d); $changeSet = $this->uow->getDocumentChangeSet($d); if (isset($changeSet['value1'])) { $d->value2 = 'v1 has changed'; @@ -30,7 +28,7 @@ public function testAutoRecompute(): void $this->dm->flush(); $this->dm->clear(); - $d = $this->dm->getRepository(get_class($d))->findOneBy(['value1' => 'Changed']); + $d = $this->dm->getRepository($d::class)->findOneBy(['value1' => 'Changed']); self::assertNotNull($d); self::assertEquals('v1 has changed', $d->value2); } @@ -43,14 +41,14 @@ public function testRefreshClearsChangeSet(): void $this->dm->flush(); $this->dm->clear(); - $d = $this->dm->getRepository(get_class($d))->findOneBy(['value1' => 'Value 1']); + $d = $this->dm->getRepository($d::class)->findOneBy(['value1' => 'Value 1']); $d->value1 = 'Changed'; - $this->uow->computeChangeSet($this->dm->getClassMetadata(get_class($d)), $d); + $this->uow->computeChangeSet($this->dm->getClassMetadata($d::class), $d); $this->dm->refresh($d); $this->dm->flush(); $this->dm->clear(); - $d = $this->dm->getRepository(get_class($d))->findOneBy(['value1' => 'Value 1']); + $d = $this->dm->getRepository($d::class)->findOneBy(['value1' => 'Value 1']); self::assertNotNull($d); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH999Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH999Test.php index 9da4217950..2549594cde 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH999Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/GH999Test.php @@ -10,8 +10,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Exception; -use function get_class; - class GH999Test extends BaseTestCase { public function testModifyingInFlushHandler(): void @@ -37,7 +35,7 @@ public function onFlush(OnFlushEventArgs $args): void foreach ($dm->getUnitOfWork()->getScheduledDocumentInsertions() as $document) { $document->setName('name #changed'); - $metadata = $dm->getClassMetadata(get_class($document)); + $metadata = $dm->getClassMetadata($document::class); $dm->getUnitOfWork()->recomputeSingleDocumentChangeSet($metadata, $document); } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php index 76491a6b73..c19eeaf608 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM116Test.php @@ -22,12 +22,12 @@ public function testIssue(): void $this->dm->flush(); $this->dm->clear(); - $parent = $this->dm->find(get_class($parent), $parent->getId()); + $parent = $this->dm->find($parent::class, $parent->getId()); $parent->getChild()->setName('ok'); $this->dm->flush(); - $check = $this->dm->getDocumentCollection(get_class($parent))->find()->toArray(); + $check = $this->dm->getDocumentCollection($parent::class)->find()->toArray(); $check = array_values($check); self::assertCount(1, $check); self::assertEquals('test', $check[0]['name']); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM166Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM166Test.php index c069991c32..8aa3a58021 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM166Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM166Test.php @@ -10,7 +10,6 @@ use Documents\Phonenumber; use Documents\User; -use function get_class; use function sort; class MODM166Test extends BaseTestCase @@ -40,7 +39,7 @@ public function testUpdateCollectionDuringOnFlushAndRecomputSingleDocumentChange $this->dm->flush(); $this->dm->clear(); - $repository = $this->dm->getRepository(get_class($test)); + $repository = $this->dm->getRepository($test::class); $test = $repository->findOneBy(['username' => 'lucy']); $phonenumbers = []; @@ -62,7 +61,7 @@ public function onFlush(OnFlushEventArgs $eventArgs): void $unitOfWork = $documentManager->getUnitOfWork(); foreach ($unitOfWork->getScheduledDocumentUpdates() as $document) { - $metadata = $documentManager->getClassMetadata(get_class($document)); + $metadata = $documentManager->getClassMetadata($document::class); $document->addPhonenumber(new Phonenumber('2222')); $unitOfWork->recomputeSingleDocumentChangeSet($metadata, $document); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM167Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM167Test.php index d2a8b015ee..1afd1101b8 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM167Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM167Test.php @@ -9,8 +9,6 @@ use Doctrine\ODM\MongoDB\Tests\BaseTestCase; use Documents\User; -use function get_class; - class MODM167Test extends BaseTestCase { private MODM167EventListener $listener; @@ -34,7 +32,7 @@ public function testDetatchNewDocumentDuringOnFlush(): void $this->dm->flush(); $this->dm->clear(); - $repository = $this->dm->getRepository(get_class($test)); + $repository = $this->dm->getRepository($test::class); $test = $repository->find($test->getId()); self::assertNull($test); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM83Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM83Test.php index 4c4985b171..19f66e3918 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM83Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM83Test.php @@ -10,8 +10,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class MODM83Test extends BaseTestCase { private MODM83EventListener $listener; @@ -67,7 +65,7 @@ class MODM83EventListener public function __call(string $method, array $args): void { $document = $args[0]->getDocument(); - $className = get_class($document); + $className = $document::class; $this->called[$method][] = $className; } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM90Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM90Test.php index 3b30104bbd..906f896509 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM90Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM90Test.php @@ -10,8 +10,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class MODM90Test extends BaseTestCase { private MODM90EventListener $listener; @@ -84,7 +82,7 @@ class MODM90EventListener public function __call(string $method, array $args): void { $document = $args[0]->getDocument(); - $className = get_class($document); + $className = $document::class; $this->called[$method][] = $className; } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM91Test.php b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM91Test.php index 9953d486cb..e8c740dc50 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM91Test.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM91Test.php @@ -10,8 +10,6 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Doctrine\ODM\MongoDB\Tests\BaseTestCase; -use function get_class; - class MODM91Test extends BaseTestCase { private MODM91EventListener $listener; @@ -59,7 +57,7 @@ class MODM91EventListener public function __call(string $method, array $args): void { $document = $args[0]->getDocument(); - $className = get_class($document); + $className = $document::class; $this->called[$method][] = $className; } } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractAnnotationDriverTestCase.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractAnnotationDriverTestCase.php index fd8f0eb5ac..b3f108aed7 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractAnnotationDriverTestCase.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AbstractAnnotationDriverTestCase.php @@ -13,8 +13,6 @@ use Generator; use stdClass; -use function get_class; - abstract class AbstractAnnotationDriverTestCase extends AbstractMappingDriverTestCase { public function testFieldInheritance(): void @@ -160,11 +158,11 @@ public function testClassCanBeMappedByOneAbstractDocument(object $wrong, string $this->expectException(MappingException::class); $this->expectExceptionMessageMatches($messageRegExp); - $cm = new ClassMetadata(get_class($wrong)); + $cm = new ClassMetadata($wrong::class); $reader = new AnnotationReader(); $annotationDriver = new AnnotationDriver($reader); - $annotationDriver->loadMetadataForClass(get_class($wrong), $cm); + $annotationDriver->loadMetadataForClass($wrong::class, $cm); } public static function provideClassCanBeMappedByOneAbstractDocument(): ?Generator diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AttributeDriverTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AttributeDriverTest.php index c33938090f..04e4adef46 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AttributeDriverTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/AttributeDriverTest.php @@ -7,7 +7,6 @@ use Doctrine\ODM\MongoDB\Mapping\Driver\AttributeDriver; use Doctrine\Persistence\Mapping\Driver\MappingDriver; -/** @requires PHP >= 8.0 */ class AttributeDriverTest extends AbstractAnnotationDriverTestCase { protected static function loadDriver(): MappingDriver diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php index 83bb27b476..501189e9f1 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/ClassMetadataTest.php @@ -41,7 +41,6 @@ use stdClass; use function array_merge; -use function get_class; use function MongoDB\BSON\fromJSON; use function MongoDB\BSON\toPHP; use function serialize; @@ -237,7 +236,7 @@ public function testEnumTypeMustPointToAnEnum(): void public $enum; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $this->expectException(MappingException::class); $this->expectExceptionMessage( @@ -257,7 +256,7 @@ public function testEnumTypeMustPointToABackedEnum(): void public $enum; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $this->expectException(MappingException::class); $this->expectExceptionMessage( @@ -435,7 +434,7 @@ public function testDefaultDiscriminatorField(): void public $assocWithDiscriminatorField; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $cm->mapField([ 'fieldName' => 'assoc', @@ -713,7 +712,7 @@ public function testAtomicCollectionUpdateUsageInEmbeddedDocument(): void public $many; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $cm->isEmbeddedDocument = true; $this->expectException(MappingException::class); @@ -733,7 +732,7 @@ public function testDefaultStorageStrategyOfEmbeddedDocumentFields(): void public $many; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $cm->isEmbeddedDocument = true; $mapping = $cm->mapField([ @@ -894,7 +893,7 @@ public function testNoIncrementFieldsAllowedInShardKey(): void public $inc; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $cm->mapField([ 'fieldName' => 'inc', 'type' => 'int', @@ -912,7 +911,7 @@ public function testNoCollectionsInShardKey(): void public $collection; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $cm->mapField([ 'fieldName' => 'collection', 'type' => 'collection', @@ -929,7 +928,7 @@ public function testNoEmbedManyInShardKey(): void public $embedMany; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $cm->mapManyEmbedded(['fieldName' => 'embedMany']); $this->expectException(MappingException::class); $this->expectExceptionMessage('No multikey indexes are allowed in the shard key'); @@ -943,7 +942,7 @@ public function testNoReferenceManyInShardKey(): void public $referenceMany; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $cm->mapManyEmbedded(['fieldName' => 'referenceMany']); $this->expectException(MappingException::class); $this->expectExceptionMessage('No multikey indexes are allowed in the shard key'); @@ -957,7 +956,7 @@ public function testArbitraryFieldInGridFSFileThrowsException(): void public $contentType; }; - $cm = new ClassMetadata(get_class($object)); + $cm = new ClassMetadata($object::class); $cm->isFile = true; $this->expectException(MappingException::class); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php index 7b01c868fb..ebafc33040 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Mapping/XmlMappingDriverTest.php @@ -12,8 +12,6 @@ use SimpleXMLElement; use stdClass; -use function get_class; - use const DIRECTORY_SEPARATOR; class XmlMappingDriverTest extends AbstractMappingDriverTestCase @@ -30,7 +28,7 @@ public function testSetShardKeyOptionsByAttributes(): void $element = new SimpleXMLElement(''); /** @uses XmlDriver::setShardKey */ - $m = new ReflectionMethod(get_class($driver), 'setShardKey'); + $m = new ReflectionMethod($driver::class, 'setShardKey'); $m->setAccessible(true); $m->invoke($driver, $class, $element); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/PersistentCollection/DefaultPersistentCollectionGeneratorTest.php b/tests/Doctrine/ODM/MongoDB/Tests/PersistentCollection/DefaultPersistentCollectionGeneratorTest.php index 68fa33774a..ea6b3cfb5b 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/PersistentCollection/DefaultPersistentCollectionGeneratorTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/PersistentCollection/DefaultPersistentCollectionGeneratorTest.php @@ -46,7 +46,6 @@ public function testWithNullableReturnType(): void self::assertInstanceOf(CollWithNullableReturnType::class, $coll); } - /** @requires PHP >= 8.0 */ public function testPHP80Types(): void { $class = $this->generator->loadClass(CollWithPHP80Types::class, Configuration::AUTOGENERATE_EVAL); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Persisters/DocumentPersisterGetShardKeyQueryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Persisters/DocumentPersisterGetShardKeyQueryTest.php index d2378325a1..2c3bd946a6 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Persisters/DocumentPersisterGetShardKeyQueryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Persisters/DocumentPersisterGetShardKeyQueryTest.php @@ -13,8 +13,6 @@ use MongoDB\BSON\UTCDateTime; use ReflectionMethod; -use function get_class; - class DocumentPersisterGetShardKeyQueryTest extends BaseTestCase { public function testGetShardKeyQueryScalars(): void @@ -25,7 +23,7 @@ public function testGetShardKeyQueryScalars(): void $o->bool = true; $o->float = 1.2; - $persister = $this->uow->getDocumentPersister(get_class($o)); + $persister = $this->uow->getDocumentPersister($o::class); $method = new ReflectionMethod($persister, 'getShardKeyQuery'); $method->setAccessible(true); @@ -43,7 +41,7 @@ public function testGetShardKeyQueryObjects(): void $o->bin = 'hi'; $o->date = new DateTime(); - $persister = $this->uow->getDocumentPersister(get_class($o)); + $persister = $this->uow->getDocumentPersister($o::class); $method = new ReflectionMethod($persister, 'getShardKeyQuery'); $method->setAccessible(true); @@ -69,7 +67,7 @@ public function testShardById(): void $o = new ShardedById(); $o->identifier = new ObjectId(); - $persister = $this->uow->getDocumentPersister(get_class($o)); + $persister = $this->uow->getDocumentPersister($o::class); $method = new ReflectionMethod($persister, 'getShardKeyQuery'); $method->setAccessible(true); @@ -88,7 +86,7 @@ public function testShardByReference(): void $this->dm->persist($o->reference); - $persister = $this->uow->getDocumentPersister(get_class($o)); + $persister = $this->uow->getDocumentPersister($o::class); $method = new ReflectionMethod($persister, 'getShardKeyQuery'); $method->setAccessible(true); diff --git a/tests/Doctrine/ODM/MongoDB/Tests/Persisters/PersistenceBuilderTest.php b/tests/Doctrine/ODM/MongoDB/Tests/Persisters/PersistenceBuilderTest.php index 0f65ee5df7..8e4ec1d6a9 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/Persisters/PersistenceBuilderTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/Persisters/PersistenceBuilderTest.php @@ -16,7 +16,6 @@ use MongoDB\BSON\ObjectId; use function array_keys; -use function get_class; class PersistenceBuilderTest extends BaseTestCase { @@ -118,7 +117,7 @@ public function testPrepareUpdateDataDoesNotIncludeId(): void $this->dm->flush(); $this->dm->clear(); - $article = $this->dm->getRepository(get_class($article))->find($article->id); + $article = $this->dm->getRepository($article::class)->find($article->id); $article->id = null; $article->topic = 'test'; @@ -157,7 +156,7 @@ public function testPrepareInsertDataWithFetchedReferenceOne(): void $this->dm->flush(); $this->dm->clear(); - $article = $this->dm->find(get_class($article), $article->id); + $article = $this->dm->find($article::class, $article->id); $comment = new CmsComment(); $comment->article = $article; @@ -182,7 +181,7 @@ public function testPrepareUpsertData(): void $this->dm->flush(); $this->dm->clear(); - $article = $this->dm->find(get_class($article), $article->id); + $article = $this->dm->find($article::class, $article->id); $comment = new CmsComment(); $comment->topic = 'test'; $comment->text = 'text'; diff --git a/tests/Doctrine/ODM/MongoDB/Tests/RepositoryFactoryTest.php b/tests/Doctrine/ODM/MongoDB/Tests/RepositoryFactoryTest.php index a0d0236677..a4b31f1bb8 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/RepositoryFactoryTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/RepositoryFactoryTest.php @@ -9,8 +9,6 @@ use Doctrine\ODM\MongoDB\Repository\RepositoryFactory; use Documents\User; -use function get_class; - class RepositoryFactoryTest extends BaseTestCase { public function testRepositoryFactoryCanBeReplaced(): void @@ -30,7 +28,7 @@ public function testRepositoriesAreSameForSameClasses(): void $proxy = $this->dm->getPartialReference(User::class, 'abc'); self::assertSame( $this->dm->getRepository(User::class), - $this->dm->getRepository(get_class($proxy)), + $this->dm->getRepository($proxy::class), ); } diff --git a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php index 0c951c3dc7..eb51e61a0b 100644 --- a/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php +++ b/tests/Doctrine/ODM/MongoDB/Tests/UnitOfWorkTest.php @@ -27,7 +27,6 @@ use ProxyManager\Proxy\GhostObjectInterface; use Throwable; -use function get_class; use function spl_object_hash; use function sprintf; @@ -88,7 +87,7 @@ public function testScheduleForUpsertWithNonObjectIdValues(): void { $doc = new UowCustomIdDocument(); $doc->id = 'string'; - $class = $this->dm->getClassMetadata(get_class($doc)); + $class = $this->dm->getClassMetadata($doc::class); self::assertFalse($this->uow->isScheduledForInsert($doc)); self::assertFalse($this->uow->isScheduledForUpsert($doc)); $this->uow->scheduleForUpsert($class, $doc); @@ -380,7 +379,7 @@ public function testPersistRemovedDocument(): void $this->uow->commit(); - self::assertNotNull($this->dm->getRepository(get_class($user))->find($user->id)); + self::assertNotNull($this->dm->getRepository($user::class)->find($user->id)); } public function testRemovePersistedButNotFlushedDocument(): void @@ -392,7 +391,7 @@ public function testRemovePersistedButNotFlushedDocument(): void $this->uow->remove($user); $this->uow->commit(); - self::assertNull($this->dm->getRepository(get_class($user))->find($user->id)); + self::assertNull($this->dm->getRepository($user::class)->find($user->id)); } public function testPersistRemovedEmbeddedDocument(): void @@ -403,7 +402,7 @@ public function testPersistRemovedEmbeddedDocument(): void $this->uow->commit(); $this->uow->clear(); - $test = $this->dm->getRepository(get_class($test))->find($test->id); + $test = $this->dm->getRepository($test::class)->find($test->id); $this->uow->remove($test); @@ -537,7 +536,7 @@ public function testCommitsInProgressIsUpdatedOnException(): void try { $this->dm->flush(); - } catch (Throwable $exception) { + } catch (Throwable) { $getCommitsInProgress = Closure::bind(fn (UnitOfWork $unitOfWork) => /** @psalm-suppress InaccessibleProperty */ $unitOfWork->commitsInProgress, $this->dm->getUnitOfWork(), UnitOfWork::class);