diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml new file mode 100644 index 0000000..cd358ad --- /dev/null +++ b/.github/workflows/coding-standards.yml @@ -0,0 +1,29 @@ +name: Lint + +on: [push] + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + extensions: json, dom, curl, libxml, mbstring + coverage: none + + - name: Install Pint + run: composer global require laravel/pint + + - name: Run Pint + run: pint + + - name: Commit linted files + uses: stefanzweifel/git-auto-commit-action@v4 + with: + commit_message: Fixed code styling diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 614da17..66835f5 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,4 +1,4 @@ -name: coverage +name: Coverage on: push: @@ -6,7 +6,7 @@ on: jobs: linux_tests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: fail-fast: true @@ -20,7 +20,7 @@ jobs: - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.1 + php-version: 8.2 extensions: dom, curl, libxml, mbstring, zip, pdo, sqlite, pdo_sqlite, gd, xdebug tools: composer:v2 coverage: xdebug diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..e1df0c5 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,31 @@ +name: Static Analysis + +on: + push: + pull_request: + +jobs: + static: + name: Static Analysis + runs-on: ubuntu-latest + strategy: + fail-fast: true + matrix: + php: ['8.1', '8.2'] + + steps: + - name: Checkout Code + uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: json, dom, curl, libxml, mbstring + coverage: none + + - name: Install Dependencies + run: composer update --prefer-stable --no-interaction --no-progress --ansi + + - name: Run Static Analysis + run: composer test:static diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml deleted file mode 100644 index d81f31d..0000000 --- a/.github/workflows/style.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: style - -on: - push: - pull_request: - -jobs: - linux_tests: - runs-on: ubuntu-20.04 - - strategy: - fail-fast: true - - name: Style Check - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.1 - extensions: dom, curl, libxml, mbstring, zip, pdo, sqlite, pdo_sqlite, gd - tools: composer:v2 - coverage: none - - - name: Install dependencies - uses: nick-invision/retry@v2 - with: - timeout_minutes: 5 - max_attempts: 2 - command: composer update --prefer-stable --prefer-dist --no-interaction --no-progress - - - name: Run PHPCS - run: vendor/bin/phpcs diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 06764e6..7a294ea 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,4 +1,4 @@ -name: tests +name: Tests on: push: @@ -6,12 +6,12 @@ on: jobs: linux_tests: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest strategy: fail-fast: true matrix: - php: ['7.3', '7.4', '8.0', '8.1'] + php: ['8.0', '8.1', '8.2'] stability: [prefer-lowest, prefer-stable] name: PHP ${{ matrix.php }} - ${{ matrix.stability }} @@ -33,7 +33,7 @@ jobs: with: timeout_minutes: 5 max_attempts: 2 - command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress ${{ matrix.flags }} + command: composer update --${{ matrix.stability }} --prefer-dist --no-interaction --no-progress - name: Execute tests run: vendor/bin/phpunit --verbose diff --git a/README.md b/README.md index 504fd99..39578c5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Laravel Relation Joins -[![Laravel Version](https://img.shields.io/badge/Laravel-8.x%2F9.x%2F10.x-blue)](https://laravel.com/) +[![Laravel Version](https://img.shields.io/badge/Laravel-9.x%2F10.x%2F11.x-blue)](https://laravel.com/) [![Build Status](https://github.com/tylernathanreed/laravel-relation-joins/workflows/tests/badge.svg)](https://github.com/tylernathanreed/laravel-relation-joins/actions) [![Style Status](https://github.com/tylernathanreed/laravel-relation-joins/workflows/style/badge.svg)](https://github.com/tylernathanreed/laravel-relation-joins/actions) [![Coverage Status](https://coveralls.io/repos/github/tylernathanreed/laravel-relation-joins/badge.svg?branch=master)](https://coveralls.io/github/tylernathanreed/laravel-relation-joins?branch=master) @@ -62,11 +62,15 @@ Reedware\LaravelRelationJoins\LaravelRelationJoinServiceProvider::class ### Versioning -This package was built with the latest version of Laravel in mind, but support goes back to Laravel 8.x. +This package is maintained with the latest version of Laravel in mind, but support follows Laravel's [Support Policy](https://laravel.com/docs/master/releases#support-policy). -For Laravel 7.x, use version 3.x of this package. -For Laravel 6.x, use version 2.x of this package. -For Laravel 5.5, use version 1.x of this package. +| Package | Laravel | PHP | +| :-----: | :--------: | :--------: | +| 5.x | 9.x - 11.x | 8.0 - 8.2+ | +| 4.x | 8.x - 10.x | 7.3 - 8.0+ | +| 3.x | 7.x - 9.x | 7.2 - 8.0+ | +| 2.x | 6.x - 8.x | 7.2 - 8.0+ | +| 1.x | 5.5 - 8.x | 7.1 - 8.0+ | ## Usage diff --git a/composer.json b/composer.json index b90f390..ad5905f 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,13 @@ { "name": "reedware/laravel-relation-joins", "description": "Adds the ability to join on a relationship by name.", - "keywords": ["laravel", "relation", "join", "eloquent", "query"], + "keywords": [ + "laravel", + "relation", + "join", + "eloquent", + "query" + ], "license": "MIT", "authors": [ { @@ -10,17 +16,18 @@ } ], "require": { - "php": ">=7.3", - "illuminate/contracts": "^8.83|^9.52|^10.0", - "illuminate/database": "^8.83|^9.52|^10.0", - "illuminate/support": "^8.83|^9.52|^10.0" + "php": ">=8.0", + "illuminate/contracts": "^9.52|^10.0", + "illuminate/database": "^9.52|^10.0", + "illuminate/support": "^9.52|^10.0" }, "require-dev": { - "illuminate/container": "^8.83|^9.52|^10.0", - "mockery/mockery": "^1.5.1", + "illuminate/container": "^9.52|^10.0", + "laravel/pint": "^1.5", + "mockery/mockery": "^1.6.6", "php-coveralls/php-coveralls": "^2.4", - "phpunit/phpunit": "^8.5.33|^9.5", - "squizlabs/php_codesniffer": "^3.6" + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^9.5" }, "autoload": { "psr-4": { @@ -38,5 +45,20 @@ "Reedware\\LaravelRelationJoins\\LaravelRelationJoinServiceProvider" ] } + }, + "minimum-stability": "stable", + "config": { + "sort-packages": true, + "preferred-install": "dist" + }, + "scripts": { + "test:coverage": [ + "@test:suite", + "php-coveralls -v --dry-run" + ], + "test:static": "phpstan", + "test:style": "pint --test", + "test:style-fix": "pint", + "test:suite": "phpunit --verbose" } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist new file mode 100644 index 0000000..f6ef1fa --- /dev/null +++ b/phpstan.neon.dist @@ -0,0 +1,33 @@ +parameters: + level: 6 + paths: + - src + excludePaths: + - tests/* + stubFiles: + - stubs/EloquentBuilder.stub + - stubs/QueryBuilder.stub + - stubs/Relation.stub + ignoreErrors: + - + message: '#Call to protected method \w+\(\) of class Illuminate\\Database\\Eloquent\\Builder.#' + path: src/Mixins/JoinsRelationships.php + - + message: '#Access to protected property Illuminate\\Database\\Eloquent\\Builder::\$\w+.#' + path: src/Mixins/JoinsRelationships.php + + + + + + + + + + + + + + + + diff --git a/src/EloquentJoinClause.php b/src/EloquentJoinClause.php index 2393481..39bcf1c 100644 --- a/src/EloquentJoinClause.php +++ b/src/EloquentJoinClause.php @@ -12,32 +12,21 @@ class EloquentJoinClause extends JoinClause { /** * The model associated to this join. - * - * @var \Illuminate\Database\Eloquent\Model */ - public $model; + public Model $model; /** * The eloquent query representing this join. - * - * @var \Illuminate\Database\Eloquent\Builder */ - public $eloquent; + public Eloquent $eloquent; /** * Whether or not a method call is being forwarded through eloquent. - * - * @var boolean */ - protected $forwardingCall = false; + protected bool $forwardingCall = false; /** * Create a new join clause instance. - * - * @param \Illuminate\Database\Query\JoinClause $parentJoin - * @param \Illuminate\Database\Eloquent\Model $model - * - * @return $this */ public function __construct(JoinClause $parentJoin, Model $model) { @@ -55,12 +44,8 @@ public function __construct(JoinClause $parentJoin, Model $model) /** * Merges the properties of the parent join into this join. - * - * @param \Illuminate\Database\Query\Builder $query - * - * @return void */ - protected function mergeQuery(Builder $query) + protected function mergeQuery(Builder $query): void { $properties = (new ReflectionClass(Builder::class))->getProperties(); @@ -77,10 +62,8 @@ protected function mergeQuery(Builder $query) /** * Apply the scopes to the eloquent builder instance and return it. - * - * @return static */ - public function applyScopes() + public function applyScopes(): static { $query = $this->eloquent->applyScopes(); @@ -91,10 +74,8 @@ public function applyScopes() /** * Returns a new query builder for the model's table. - * - * @return \Illuminate\Database\Eloquent\Builder */ - public function newEloquentQuery() + public function newEloquentQuery(): Eloquent { return $this->model->registerGlobalScopes( $this->newModelQuery() @@ -103,30 +84,24 @@ public function newEloquentQuery() /** * Returns a new eloquent builder that doesn't have any global scopes or eager loading. - * - * @return \Illuminate\Database\Eloquent\Builder */ - public function newModelQuery() + public function newModelQuery(): Eloquent { return $this->newEloquentBuilder()->setModel($this->model); } /** * Returns a new eloquent builder for this join clause. - * - * @return \Illuminate\Database\Eloquent\Builder */ - public function newEloquentBuilder() + public function newEloquentBuilder(): Eloquent { return new Eloquent($this); } /** * Get a new instance of the join clause builder. - * - * @return \Illuminate\Database\Query\JoinClause */ - public function newQuery() + public function newQuery(): JoinClause { return new JoinClause($this->newParentQuery(), $this->type, $this->table); } @@ -134,11 +109,8 @@ public function newQuery() /** * Handle dynamic method calls into the method. * - * @phpcs:disable Squiz.Commenting.FunctionComment - * * @param string $method - * @param array $parameters - * + * @param array $parameters * @return mixed */ public function __call($method, $parameters) diff --git a/src/LaravelRelationJoinServiceProvider.php b/src/LaravelRelationJoinServiceProvider.php index 51dc402..135251a 100644 --- a/src/LaravelRelationJoinServiceProvider.php +++ b/src/LaravelRelationJoinServiceProvider.php @@ -11,10 +11,8 @@ class LaravelRelationJoinServiceProvider extends ServiceProvider { /** * Bootstrap any application services. - * - * @return void */ - public function boot() + public function boot(): void { Query::mixin(new Mixins\MergeJoins); Query::mixin(new Mixins\JoinOperations); diff --git a/src/Mixins/JoinOperations.php b/src/Mixins/JoinOperations.php index c193e89..fd815ed 100644 --- a/src/Mixins/JoinOperations.php +++ b/src/Mixins/JoinOperations.php @@ -3,54 +3,44 @@ namespace Reedware\LaravelRelationJoins\Mixins; use Closure; +use Illuminate\Database\Query\Builder; +/** @mixin Builder */ class JoinOperations { /** * Defines the mixin for {@see $query->on()}. - * - * @return \Closure */ - public function on() + public function on(): Closure { /** * Add an "on" clause to the join. - * - * @param \Closure|string $first - * @param string|null $operator - * @param string|null $second - * @param string $boolean - * - * @return $this */ - return function ($first, $operator = null, $second = null, $boolean = 'and') { - + return function ( + Closure|string $first, + string $operator = null, + string $second = null, + string $boolean = 'and' + ): Builder { + /** @var Builder $this */ if ($first instanceof Closure) { return $this->whereNested($first, $boolean); } return $this->whereColumn($first, $operator, $second, $boolean); - }; } /** * Defines the mixin for {@see $query->orOn()}. - * - * @return \Closure */ - public function orOn() + public function orOn(): Closure { /** * Add an "or on" clause to the join. - * - * @param \Closure|string $first - * @param string|null $operator - * @param string|null $second - * - * @return $this */ - return function ($first, $operator = null, $second = null) { + return function (Closure|string $first, string $operator = null, string $second = null): Builder { + /** @var Builder $this */ return $this->on($first, $operator, $second, 'or'); }; } diff --git a/src/Mixins/JoinsRelationships.php b/src/Mixins/JoinsRelationships.php index 12fdb89..c980ca9 100644 --- a/src/Mixins/JoinsRelationships.php +++ b/src/Mixins/JoinsRelationships.php @@ -3,39 +3,42 @@ namespace Reedware\LaravelRelationJoins\Mixins; use Closure; -use Illuminate\Support\Arr; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\Relation; +use Illuminate\Database\Query\Builder as Query; use Illuminate\Database\Query\JoinClause; +use Illuminate\Support\Arr; use LogicException; use Reedware\LaravelRelationJoins\EloquentJoinClause; use Reedware\LaravelRelationJoins\MorphTypes; use RuntimeException; +/** @mixin Builder */ class JoinsRelationships { /** * Defines the mixin for {@see $query->joinRelation()}. - * - * @return \Closure */ - public function joinRelation() + public function joinRelation(): Closure { /** * Add a relationship join condition to the query. * - * @param mixed $relation - * @param \Closure|array|null $callback - * @param string $type - * @param bool $through - * @param \Illuminate\Database\Eloquent\Builder $relatedQuery - * @param mixed $morphTypes - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Relation|string|array $relation + * @param Closure|array|null $callback + * @param MorphTypes|array|string $morphTypes */ - return function ($relation, $callback = null, $type = 'inner', $through = false, Builder $relatedQuery = null, $morphTypes = ['*']) { - + return function ( + Relation|string|array $relation, + Closure|array $callback = null, + string $type = 'inner', + bool $through = false, + Builder $relatedQuery = null, + MorphTypes|array|string $morphTypes = ['*'] + ): Builder { + /** @var Builder $this */ if (! $morphTypes instanceof MorphTypes) { $morphTypes = new MorphTypes($morphTypes); } @@ -54,11 +57,13 @@ public function joinRelation() $relation = ($relatedQuery ?: $this)->getRelationWithoutConstraints($relationName); if (! $relation instanceof Relation) { - throw new LogicException(sprintf('%s::%s must return a relationship instance.', get_class($this->getModel()), $relationName)); + throw new LogicException(sprintf( + '%s::%s must return a relationship instance.', + get_class($this->getModel()), + $relationName) + ); } - } - - else if (is_array($relation)) { + } elseif (is_array($relation)) { [$relation, $alias] = $relation; } @@ -104,24 +109,22 @@ public function joinRelation() /** * Defines the mixin for {@see $query->joinNestedRelation()}. - * - * @return \Closure */ - protected function joinNestedRelation() + public function joinNestedRelation(): Closure { /** * Add nested relationship join conditions to the query. * - * @param string $relations - * @param \Closure|array|null $callbacks - * @param string $type - * @param bool $through - * @param \Reedware\LaravelRelationJoins\MorphTypes $morphTypes - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Closure|array|null $callbacks */ - return function ($relations, $callbacks, $type, $through, MorphTypes $morphTypes) { - + return function ( + string $relations, + Closure|array|null $callbacks, + string $type, + bool $through, + MorphTypes $morphTypes + ): Builder { + /** @var Builder $this */ $relations = explode('.', $relations); $relatedQuery = $this; @@ -139,30 +142,30 @@ protected function joinNestedRelation() $callback = $callbacks[$relation] ?? null; $useThrough = count($relations) > 0 && $through; - $relatedQuery = $this->joinRelation($relation, $callback, $type, $useThrough, $relatedQuery, $morphTypes); + $relatedQuery = $this->joinRelation( + $relation, + $callback, + $type, + $useThrough, + $relatedQuery, + $morphTypes + ); } return $this; - }; } /** * Defines the mixin for {@see $query->applyJoinScopes()}. - * - * @return \Closure */ - protected function applyJoinScopes() + public function applyJoinScopes(): Closure { /** * Applies the eloquent scopes to the specified query. - * - * @param \Illuminate\Database\Query\Builder $joinQuery - * - * @return \Illuminate\Database\Query\Builder */ - return function (Builder $joinQuery) { - + return function (Builder $joinQuery): Builder { + /** @var Builder $this */ $joins = $joinQuery->getQuery()->joins ?: []; foreach ($joins as $join) { @@ -172,27 +175,19 @@ protected function applyJoinScopes() } return $joinQuery; - }; } /** * Defines the mixin for {@see $query->callJoinScope()}. - * - * @return \Closure */ - protected function callJoinScope() + public function callJoinScope(): Closure { /** * Calls the provided callback on the join query. - * - * @param \Illuminate\Database\Query\Builder $joinQuery - * @param \Closure $callback - * - * @return void */ - return function (Builder $joinQuery, Closure $callback) { - + return function (Builder $joinQuery, Closure $callback): void { + /** @var Builder $this */ $joins = $joinQuery->getQuery()->joins ?: []; array_unshift($joins, $joinQuery); @@ -230,23 +225,19 @@ protected function callJoinScope() foreach ($queries as $query) { $joinQuery->addBinding($query->getBindings(), 'join'); } - }; } /** * Defines the mixin for {@see $query->getJoinType()}. - * - * @return \Closure */ - protected function getJoinType() + public function getJoinType(): Closure { /** * Returns the custom provided join type. - * - * @return string|null */ - return function () { + return function (): ?string { + /** @var Builder $this */ if (! property_exists($this, 'type')) { return null; } @@ -265,7 +256,7 @@ protected function getJoinType() // @codeCoverageIgnoreStart finally { - // @codeCoverageIgnoreEnd + // @codeCoverageIgnoreEnd return $type; } }; @@ -273,22 +264,14 @@ protected function getJoinType() /** * Defines the mixin for {@see $query->addJoinRelationWhere()}. - * - * @return \Closure */ - protected function addJoinRelationWhere() + public function addJoinRelationWhere(): Closure { /** * Add the "join relation" condition where clause to the query. - * - * @param \Illuminate\Database\Eloquent\Builder $joinQuery - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation - * @param string $type - * - * @return \Illuminate\Database\Eloquent\Builder|static */ - return function (Builder $joinQuery, Relation $relation, $type) { - + return function (Builder $joinQuery, Relation $relation, string $type): Builder { + /** @var Builder $this */ $joinQuery->mergeConstraintsFrom($relation->getQuery()); $baseJoinQuery = $joinQuery->toBase(); @@ -310,79 +293,31 @@ protected function addJoinRelationWhere() }, null, null, $type); return $this; - - }; - } - - /** - * Defines the mixin for {@see $query->replaceWhereNestedQueryBuildersWithJoinBuilders()}. - * - * @return \Closure - */ - protected function replaceWhereNestedQueryBuildersWithJoinBuilders() - { - /** - * Replaces the query builders in nested "where" clauses with join builders. - * - * @param \Illuminate\Database\Query\Builder $query - * - * @return void - */ - return function ($query) { - - $wheres = $query->wheres; - - $wheres = array_map(function ($where) { - if (! isset($where['query'])) { - return $where; - } - - if ($where['type'] == 'Exists' || $where['type'] == 'NotExists') { - return $where; - } - - $this->replaceWhereNestedQueryBuildersWithJoinBuilders($where['query']); - - $joinClause = new JoinClause($where['query'], 'inner', $where['query']->from); - - foreach (array_keys(get_object_vars($where['query'])) as $key) { - $joinClause->{$key} = $where['query']->{$key}; - } - - $where['query'] = $joinClause; - - return $where; - }, $wheres); - - $query->wheres = $wheres; - }; } /** * Defines the mixin for {@see $query->getBelongsToJoinRelation()}. - * - * @return \Closure */ - protected function getBelongsToJoinRelation() + public function getBelongsToJoinRelation(): Closure { /** - * Description. - * - * @param \Illuminate\Database\Eloquent\Relations\MorphTo $relation - * @param \Reedware\LaravelRelationJoins\MorphTypes $morphTypes - * @param \Illuminate\Database\Eloquent\Builder $relatedQuery - * - * @return array + * Returns the belongs to relation for the next morph. */ - return function (MorphTo $relation, MorphTypes $morphTypes, Builder $relatedQuery) { + return function (MorphTo $relation, MorphTypes $morphTypes, Builder $relatedQuery): BelongsTo { + /** @var Builder $this */ // When it comes to joining across morph types, we can really only support // a single type. However, when we're provided multiple types, we will // instead use these one at a time and pass the information along. if ($morphTypes->items === ['*']) { - $types = $relatedQuery->model->distinct()->pluck($relation->getMorphType())->filter()->all(); + $types = $relatedQuery->model + ->newQuery() + ->distinct() + ->pluck($relation->getMorphType()) + ->filter() + ->all(); $types = array_unique(array_map(function ($morphType) { return Relation::getMorphedModel($morphType) ?? $morphType; @@ -410,325 +345,300 @@ protected function getBelongsToJoinRelation() ); return $belongsTo; - }; } /** * Defines the mixin for {@see $query->leftJoinRelation()}. - * - * @return \Closure */ - public function leftJoinRelation() + public function leftJoinRelation(): Closure { /** * Add a relationship left join condition to the query. * - * @param string $relation - * @param \Closure|array|null $callback - * @param bool $through - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Closure|array|null $callback */ - return function ($relation, $callback = null, $through = false) { + return function (string $relation, Closure|array $callback = null, bool $through = false): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'left', $through); }; } /** * Defines the mixin for {@see $query->rightJoinRelation()}. - * - * @return \Closure */ - public function rightJoinRelation() + public function rightJoinRelation(): Closure { /** * Add a relationship right join condition to the query. * - * @param string $relation - * @param \Closure|array|null $callback - * @param bool $through - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Closure|array|null $callback */ - return function ($relation, $callback = null, $through = false) { + return function (string $relation, Closure|array $callback = null, bool $through = false): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'right', $through); }; } /** * Defines the mixin for {@see $query->crossJoinRelation()}. - * - * @return \Closure */ - public function crossJoinRelation() + public function crossJoinRelation(): Closure { /** * Add a relationship cross join condition to the query. * - * @param string $relation - * @param \Closure|array|null $callback - * @param bool $through - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Closure|array|null $callback */ - return function ($relation, $callback = null, $through = false) { + return function (string $relation, Closure|array $callback = null, bool $through = false): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'cross', $through); }; } /** * Defines the mixin for {@see $query->joinThroughRelation()}. - * - * @return \Closure */ - public function joinThroughRelation() + public function joinThroughRelation(): Closure { /** * Add a relationship join condition through a related model to the query. * - * @param string $relation - * @param \Closure|array|null $callback - * @param string $type - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Closure|array|null $callback */ - return function ($relation, $callback = null, $type = 'inner') { + return function (string $relation, Closure|array $callback = null, string $type = 'inner'): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, $type, true); }; } /** * Defines the mixin for {@see $query->leftJoinThroughRelation()}. - * - * @return \Closure */ - public function leftJoinThroughRelation() + public function leftJoinThroughRelation(): Closure { /** * Add a relationship left join condition through a related model to the query. * - * @param string $relation - * @param \Closure|array|null $callback - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Closure|array|null $callback */ - return function ($relation, $callback = null) { + return function (string $relation, Closure|array $callback = null): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'left', true); }; } /** * Defines the mixin for {@see $query->rightJoinThroughRelation()}. - * - * @return \Closure */ - public function rightJoinThroughRelation() + public function rightJoinThroughRelation(): Closure { /** * Add a relationship right join condition through a related model to the query. * - * @param string $relation - * @param \Closure|array|null $callback - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Closure|array|null $callback */ - return function ($relation, $callback = null) { + return function (string $relation, Closure|array $callback = null): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'right', true); }; } /** * Defines the mixin for {@see $query->crossJoinThroughRelation()}. - * - * @return \Closure */ - public function crossJoinThroughRelation() + public function crossJoinThroughRelation(): Closure { /** * Add a relationship cross join condition through a related model to the query. * - * @param string $relation - * @param \Closure|array|null $callback - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param Closure|array|null $callback */ - return function ($relation, $callback = null) { + return function (string $relation, Closure|array $callback = null): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'cross', true); }; } /** * Defines the mixin for {@see $query->joinMorphRelation()}. - * - * @return \Closure */ - public function joinMorphRelation() + public function joinMorphRelation(): Closure { /** * Add a morph to relationship join condition to the query. * - * @param string|array $relation - * @param string|array $morphTypes - * @param \Closure|array|null $callback - * @param string $type - * @param bool $through - * @param \Illuminate\Database\Eloquent\Builder $relatedQuery - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param string|array $relation + * @param array|string $morphTypes + * @param Closure|array|null $callback */ - return function ($relation, $morphTypes = ['*'], $callback = null, $type = 'inner', $through = false, Builder $relatedQuery = null) { + return function ( + string|array $relation, + array|string $morphTypes = ['*'], + Closure|array $callback = null, + string $type = 'inner', + bool $through = false, + Builder $relatedQuery = null + ): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, $type, $through, $relatedQuery, $morphTypes); }; } /** * Defines the mixin for {@see $query->leftJoinMorphRelation()}. - * - * @return \Closure */ - public function leftJoinMorphRelation() + public function leftJoinMorphRelation(): Closure { /** * Add a morph to relationship left join condition to the query. * - * @param string $relation - * @param string|array $morphTypes - * @param \Closure|array|null $callback - * @param bool $through - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param string|array $relation + * @param array|string $morphTypes + * @param Closure|array|null $callback */ - return function ($relation, $morphTypes = ['*'], $callback = null, $through = false) { + return function ( + string|array $relation, + array|string $morphTypes = ['*'], + Closure|array $callback = null, + bool $through = false + ): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'left', $through, null, $morphTypes); }; } /** * Defines the mixin for {@see $query->rightJoinMorphRelation()}. - * - * @return \Closure */ - public function rightJoinMorphRelation() + public function rightJoinMorphRelation(): Closure { /** * Add a morph to relationship right join condition to the query. * - * @param string $relation - * @param string|array $morphTypes - * @param \Closure|array|null $callback - * @param bool $through - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param string|array $relation + * @param array|string $morphTypes + * @param Closure|array|null $callback */ - return function ($relation, $morphTypes = ['*'], $callback = null, $through = false) { + return function ( + string|array $relation, + array|string $morphTypes = ['*'], + Closure|array $callback = null, + bool $through = false + ): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'right', $through, null, $morphTypes); }; } /** * Defines the mixin for {@see $query->crossJoinMorphRelation()}. - * - * @return \Closure */ - public function crossJoinMorphRelation() + public function crossJoinMorphRelation(): Closure { /** * Add a morph to relationship cross join condition to the query. * - * @param string $relation - * @param string|array $morphTypes - * @param \Closure|array|null $callback - * @param bool $through - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param string|array $relation + * @param array|string $morphTypes + * @param Closure|array|null $callback */ - return function ($relation, $morphTypes = ['*'], $callback = null, $through = false) { + return function ( + string|array $relation, + array|string $morphTypes = ['*'], + Closure|array $callback = null, + bool $through = false + ): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'cross', $through, null, $morphTypes); }; } /** * Defines the mixin for {@see $query->joinThroughMorphRelation()}. - * - * @return \Closure */ - public function joinThroughMorphRelation() + public function joinThroughMorphRelation(): Closure { /** * Add a morph to relationship join condition through a related model to the query. * - * @param string $relation - * @param string|array $morphTypes - * @param \Closure|array|null $callback - * @param string $type - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param string|array $relation + * @param array|string $morphTypes + * @param Closure|array|null $callback */ - return function ($relation, $morphTypes = ['*'], $callback = null, $type = 'inner') { + return function ( + string|array $relation, + array|string $morphTypes = ['*'], + Closure|array $callback = null, + string $type = 'inner' + ): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, $type, true, null, $morphTypes); }; } /** * Defines the mixin for {@see $query->leftJoinThroughMorphRelation()}. - * - * @return \Closure */ - public function leftJoinThroughMorphRelation() + public function leftJoinThroughMorphRelation(): Closure { /** * Add a morph to relationship left join condition through a related model to the query. * - * @param string $relation - * @param string|array $morphTypes - * @param \Closure|array|null $callback - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param string|array $relation + * @param array|string $morphTypes + * @param Closure|array|null $callback */ - return function ($relation, $morphTypes = ['*'], $callback = null) { + return function ( + string|array $relation, + array|string $morphTypes = ['*'], + Closure|array $callback = null + ): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'left', true, null, $morphTypes); }; } /** * Defines the mixin for {@see $query->rightJoinThroughMorphRelation()}. - * - * @return \Closure */ - public function rightJoinThroughMorphRelation() + public function rightJoinThroughMorphRelation(): Closure { /** * Add a morph to relationship right join condition through a related model to the query. * - * @param string $relation - * @param string|array $morphTypes - * @param \Closure|array|null $callback - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param string|array $relation + * @param array|string $morphTypes + * @param Closure|array|null $callback */ - return function ($relation, $morphTypes = ['*'], $callback = null) { + return function ( + string|array $relation, + array|string $morphTypes = ['*'], + Closure|array $callback = null + ): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'right', true, null, $morphTypes); }; } /** * Defines the mixin for {@see $query->crossJoinThroughMorphRelation()}. - * - * @return \Closure */ - public function crossJoinThroughMorphRelation() + public function crossJoinThroughMorphRelation(): Closure { /** * Add a morph to relationship cross join condition through a related model to the query. * - * @param string $relation - * @param string|array $morphTypes - * @param \Closure|array|null $callback - * - * @return \Illuminate\Database\Eloquent\Builder|static + * @param string|array $relation + * @param array|string $morphTypes + * @param Closure|array|null $callback */ - return function ($relation, $morphTypes = ['*'], $callback = null) { + return function ( + string|array $relation, + array|string $morphTypes = ['*'], + Closure|array $callback = null + ): Builder { + /** @var Builder $this */ return $this->joinRelation($relation, $callback, 'cross', true, null, $morphTypes); }; } diff --git a/src/Mixins/MergeJoins.php b/src/Mixins/MergeJoins.php index de8458f..7ae6e2f 100644 --- a/src/Mixins/MergeJoins.php +++ b/src/Mixins/MergeJoins.php @@ -2,24 +2,23 @@ namespace Reedware\LaravelRelationJoins\Mixins; +use Closure; +use Illuminate\Database\Query\Builder; +use Illuminate\Database\Query\JoinClause; + +/** @mixin Builder */ class MergeJoins { /** * Defines the mixin for {@see $query->mergeJoins()}. - * - * @return \Closure */ - public function mergeJoins() + public function mergeJoins(): Closure { /** * Merges an array of join clauses and bindings. - * - * @param array $joins - * @param array $bindings - * - * @return void */ - return function ($joins, $bindings) { + return function (array $joins, array $bindings): void { + /** @var Builder $this */ $this->joins = array_merge($this->joins ?: [], (array) $joins); $this->bindings['join'] = array_values( @@ -27,4 +26,42 @@ public function mergeJoins() ); }; } + + /** + * Defines the mixin for {@see $query->replaceWhereNestedQueryBuildersWithJoinBuilders()}. + */ + public function replaceWhereNestedQueryBuildersWithJoinBuilders(): Closure + { + /** + * Replaces the query builders in nested "where" clauses with join builders. + */ + return function (Builder $query): void { + /** @var Builder $this */ + $wheres = $query->wheres; + + $wheres = array_map(function ($where) { + if (! isset($where['query'])) { + return $where; + } + + if ($where['type'] == 'Exists' || $where['type'] == 'NotExists') { + return $where; + } + + $this->replaceWhereNestedQueryBuildersWithJoinBuilders($where['query']); + + $joinClause = new JoinClause($where['query'], 'inner', $where['query']->from); + + foreach (array_keys(get_object_vars($where['query'])) as $key) { + $joinClause->{$key} = $where['query']->{$key}; + } + + $where['query'] = $joinClause; + + return $where; + }, $wheres); + + $query->wheres = $wheres; + }; + } } diff --git a/src/Mixins/RelationJoinQueries.php b/src/Mixins/RelationJoinQueries.php index 69c2738..bb7b77a 100644 --- a/src/Mixins/RelationJoinQueries.php +++ b/src/Mixins/RelationJoinQueries.php @@ -2,29 +2,24 @@ namespace Reedware\LaravelRelationJoins\Mixins; +use Closure; use Illuminate\Database\Eloquent\Builder; +use Illuminate\Database\Eloquent\Relations\Relation; use Reedware\LaravelRelationJoins\RelationJoinQuery; +/** @mixin Relation */ class RelationJoinQueries { /** * Defines the mixin for {@see $relation->getRelationJoinQuery()}. - * - * @return \Closure */ - public function getRelationJoinQuery() + public function getRelationJoinQuery(): Closure { /** * Adds the constraints for a relationship join. - * - * @param \Illuminate\Database\Eloquent\Builder $query - * @param \Illuminate\Database\Eloquent\Builder $parentQuery - * @param string $type - * @param string|null $alias - * - * @return \Illuminate\Database\Eloquent\Builder */ - return function (Builder $query, Builder $parentQuery, $type = 'inner', $alias = null) { + return function (Builder $query, Builder $parentQuery, string $type = 'inner', string $alias = null): Builder { + /** @var Relation $this */ return RelationJoinQuery::get($this, $query, $parentQuery, $type, $alias); }; } diff --git a/src/MorphTypes.php b/src/MorphTypes.php index a75a847..a949e8d 100644 --- a/src/MorphTypes.php +++ b/src/MorphTypes.php @@ -7,16 +7,14 @@ class MorphTypes /** * The underlying morph types. * - * @var array + * @var array */ - public $items; + public array $items; /** * Creates a new morph types instance. * - * @param string|array $items - * - * @return $this; + * @param string|array $items */ public function __construct($items) { diff --git a/src/RelationJoinQuery.php b/src/RelationJoinQuery.php index dfcd536..2999c6d 100644 --- a/src/RelationJoinQuery.php +++ b/src/RelationJoinQuery.php @@ -20,97 +20,74 @@ class RelationJoinQuery { /** * Adds the constraints for a relationship join. - * - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation - * @param \Illuminate\Database\Eloquent\Builder $query - * @param \Illuminate\Database\Eloquent\Builder $parentQuery - * @param string $type - * @param string|null $alias - * - * @return \Illuminate\Database\Eloquent\Builder */ - public static function get(Relation $relation, Builder $query, Builder $parentQuery, string $type = 'inner', string $alias = null) - { + public static function get( + Relation $relation, + Builder $query, + Builder $parentQuery, + string $type = 'inner', + string $alias = null + ): Builder { if ($relation instanceof BelongsTo) { return static::belongsTo($relation, $query, $parentQuery, $type, $alias); - } - - else if ($relation instanceof MorphToMany) { + } elseif ($relation instanceof MorphToMany) { return static::morphToMany($relation, $query, $parentQuery, $type, $alias); - } - - else if ($relation instanceof BelongsToMany) { + } elseif ($relation instanceof BelongsToMany) { return static::belongsToMany($relation, $query, $parentQuery, $type, $alias); - } - - else if ($relation instanceof HasMany) { + } elseif ($relation instanceof HasMany) { return static::hasOneOrMany($relation, $query, $parentQuery, $type, $alias); - } - - else if ($relation instanceof HasOneThrough) { + } elseif ($relation instanceof HasOneThrough) { return static::hasOneOrManyThrough($relation, $query, $parentQuery, $type, $alias); - } - - else if ($relation instanceof HasManyThrough) { + } elseif ($relation instanceof HasManyThrough) { return static::hasOneOrManyThrough($relation, $query, $parentQuery, $type, $alias); - } - - else if ($relation instanceof HasOne) { + } elseif ($relation instanceof HasOne) { return static::hasOneOrMany($relation, $query, $parentQuery, $type, $alias); - } - - else if ($relation instanceof MorphMany) { + } elseif ($relation instanceof MorphMany) { return static::morphOneOrMany($relation, $query, $parentQuery, $type, $alias); - } - - else if ($relation instanceof MorphOne) { + } elseif ($relation instanceof MorphOne) { return static::morphOneOrMany($relation, $query, $parentQuery, $type, $alias); } - throw new InvalidArgumentException('Unsupported relation type [' . get_class($relation) . '].'); + throw new InvalidArgumentException('Unsupported relation type ['.get_class($relation).'].'); } /** * Adds the constraints for a belongs to relationship join. - * - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation - * @param \Illuminate\Database\Eloquent\Builder $query - * @param \Illuminate\Database\Eloquent\Builder $parentQuery - * @param string $type - * @param string|null $alias - * - * @return \Illuminate\Database\Eloquent\Builder */ - protected static function belongsTo(Relation $relation, Builder $query, Builder $parentQuery, string $type = 'inner', string $alias = null) - { + protected static function belongsTo( + BelongsTo $relation, + Builder $query, + Builder $parentQuery, + string $type = 'inner', + string $alias = null + ): Builder { if (is_null($alias) && $query->getQuery()->from == $parentQuery->getQuery()->from) { $alias = $relation->getRelationCountHash(); } if (! is_null($alias) && $alias != $query->getModel()->getTable()) { - $query->from($query->getModel()->getTable() . ' as ' . $alias); + $query->from($query->getModel()->getTable().' as '.$alias); $query->getModel()->setTable($alias); } - return $query->whereColumn( + $query->whereColumn( $relation->getQualifiedOwnerKeyName(), '=', $relation->getQualifiedForeignKeyName() ); + + return $query; } /** * Adds the constraints for a belongs to many relationship join. - * - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation - * @param \Illuminate\Database\Eloquent\Builder $query - * @param \Illuminate\Database\Eloquent\Builder $parentQuery - * @param string $type - * @param string|null $alias - * - * @return \Illuminate\Database\Eloquent\Builder */ - protected static function belongsToMany(Relation $relation, Builder $query, Builder $parentQuery, string $type = 'inner', string $alias = null) - { + protected static function belongsToMany( + BelongsToMany $relation, + Builder $query, + Builder $parentQuery, + string $type = 'inner', + string $alias = null + ): Builder { if (! is_null($alias) && strpos($alias, ',') !== false) { [$pivotAlias, $farAlias] = explode(',', $alias); } else { @@ -122,13 +99,13 @@ protected static function belongsToMany(Relation $relation, Builder $query, Buil } if (! is_null($farAlias) && $farAlias != $relation->getRelated()->getTable()) { - $query->from($relation->getRelated()->getTable() . ' as ' . $farAlias); + $query->from($relation->getRelated()->getTable().' as '.$farAlias); $relation->getRelated()->setTable($farAlias); } if (! is_null($pivotAlias) && $pivotAlias != $relation->getTable()) { - $table = $relation->getTable() . ' as ' . $pivotAlias; + $table = $relation->getTable().' as '.$pivotAlias; $on = $pivotAlias; } else { @@ -136,7 +113,7 @@ protected static function belongsToMany(Relation $relation, Builder $query, Buil } $query->join($table, function ($join) use ($relation, $on) { - $join->on($on . '.' . $relation->getForeignPivotKeyName(), '=', $relation->getQualifiedParentKeyName()); + $join->on($on.'.'.$relation->getForeignPivotKeyName(), '=', $relation->getQualifiedParentKeyName()); }, null, null, $type); // When a belongs to many relation uses an eloquent model to define the pivot @@ -150,37 +127,40 @@ protected static function belongsToMany(Relation $relation, Builder $query, Buil ); } - return $query->whereColumn( - $relation->getRelated()->qualifyColumn($relation->getRelatedKeyName()), '=', $on . '.' . $relation->getRelatedPivotKeyName() + $query->whereColumn( + $relation->getRelated()->qualifyColumn($relation->getRelatedKeyName()), + '=', + $on.'.'.$relation->getRelatedPivotKeyName() ); + + return $query; } /** * Adds the constraints for a has one or has many relationship join. - * - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation - * @param \Illuminate\Database\Eloquent\Builder $query - * @param \Illuminate\Database\Eloquent\Builder $parentQuery - * @param string $type - * @param string|null $alias - * - * @return \Illuminate\Database\Eloquent\Builder */ - protected static function hasOneOrMany(Relation $relation, Builder $query, Builder $parentQuery, string $type = 'inner', string $alias = null) - { + protected static function hasOneOrMany( + HasOne|HasMany|MorphOne|MorphMany $relation, + Builder $query, + Builder $parentQuery, + string $type = 'inner', + string $alias = null + ): Builder { if (is_null($alias) && $query->getQuery()->from == $parentQuery->getQuery()->from) { $alias = $relation->getRelationCountHash(); } if (! is_null($alias) && $alias != $query->getModel()->getTable()) { - $query->from($query->getModel()->getTable() . ' as ' . $alias); + $query->from($query->getModel()->getTable().' as '.$alias); $query->getModel()->setTable($alias); } - return $query->whereColumn( + $query->whereColumn( $query->qualifyColumn($relation->getForeignKeyName()), '=', $relation->getQualifiedParentKeyName() ); + + return $query; } /** @@ -191,17 +171,14 @@ protected static function hasOneOrMany(Relation $relation, Builder $query, Build * is nearing EoL, and 7.x is already EoL, we'll let it slide for now. * * @see https://github.com/laravel/framework/commit/de4c42f04d609b119a4e0a7e6223c37bfe54cb87 - * - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation - * @param \Illuminate\Database\Eloquent\Builder $query - * @param \Illuminate\Database\Eloquent\Builder $parentQuery - * @param string $type - * @param string|null $alias - * - * @return \Illuminate\Database\Eloquent\Builder */ - protected static function hasOneOrManyThrough(Relation $relation, Builder $query, Builder $parentQuery, string $type = 'inner', string $alias = null) - { + protected static function hasOneOrManyThrough( + HasOne|HasManyThrough $relation, + Builder $query, + Builder $parentQuery, + string $type = 'inner', + string $alias = null + ): Builder { if (! is_null($alias) && strpos($alias, ',') !== false) { [$throughAlias, $farAlias] = explode(',', $alias); } else { @@ -217,13 +194,13 @@ protected static function hasOneOrManyThrough(Relation $relation, Builder $query } if (! is_null($farAlias) && $farAlias != $query->getModel()->getTable()) { - $query->from($query->getModel()->getTable() . ' as ' . $farAlias); + $query->from($query->getModel()->getTable().' as '.$farAlias); $query->getModel()->setTable($farAlias); } if (! is_null($throughAlias) && $throughAlias != $relation->getParent()->getTable()) { - $table = $relation->getParent()->getTable() . ' as ' . $throughAlias; + $table = $relation->getParent()->getTable().' as '.$throughAlias; $on = $throughAlias; } else { @@ -231,7 +208,11 @@ protected static function hasOneOrManyThrough(Relation $relation, Builder $query } $query->join($table, function ($join) use ($relation, $parentQuery, $on) { - $join->on($on . '.' . $relation->getFirstKeyName(), '=', $parentQuery->qualifyColumn($relation->getLocalKeyName())); + $join->on( + $on.'.'.$relation->getFirstKeyName(), + '=', + $parentQuery->qualifyColumn($relation->getLocalKeyName()) + ); }, null, null, $type); // The has one/many through relations use an eloquent model to define the step @@ -243,26 +224,25 @@ protected static function hasOneOrManyThrough(Relation $relation, Builder $query $relation->getParent()->newInstance()->setTable($on) ); - return $query->whereColumn( - $relation->getQualifiedForeignKeyName(), '=', $on . '.' . $relation->getSecondLocalKeyName() + $query->whereColumn( + $relation->getQualifiedForeignKeyName(), '=', $on.'.'.$relation->getSecondLocalKeyName() ); + + return $query; } /** * Adds the constraints for a morph one or morph many relationship join. - * - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation - * @param \Illuminate\Database\Eloquent\Builder $query - * @param \Illuminate\Database\Eloquent\Builder $parentQuery - * @param string $type - * @param string|null $alias - * - * @return \Illuminate\Database\Eloquent\Builder */ - protected static function morphOneOrMany(Relation $relation, Builder $query, Builder $parentQuery, string $type = 'inner', string $alias = null) - { + protected static function morphOneOrMany( + MorphOne|MorphMany $relation, + Builder $query, + Builder $parentQuery, + string $type = 'inner', + string $alias = null + ): Builder { if (! is_null($alias) && $alias != $relation->getRelated()->getTable()) { - $query->from($relation->getRelated()->getTable() . ' as ' . $alias); + $query->from($relation->getRelated()->getTable().' as '.$alias); $relation->getRelated()->setTable($alias); } @@ -274,17 +254,14 @@ protected static function morphOneOrMany(Relation $relation, Builder $query, Bui /** * Adds the constraints for a morph to many relationship join. - * - * @param \Illuminate\Database\Eloquent\Relations\Relation $relation - * @param \Illuminate\Database\Eloquent\Builder $query - * @param \Illuminate\Database\Eloquent\Builder $parentQuery - * @param string $type - * @param string|null $alias - * - * @return \Illuminate\Database\Eloquent\Builder */ - protected static function morphToMany(Relation $relation, Builder $query, Builder $parentQuery, string $type = 'inner', string $alias = null) - { + protected static function morphToMany( + MorphToMany $relation, + Builder $query, + Builder $parentQuery, + string $type = 'inner', + string $alias = null + ): Builder { if (! is_null($alias) && strpos($alias, ',') !== false) { [$pivotAlias, $farAlias] = explode(',', $alias); } else { @@ -296,25 +273,23 @@ protected static function morphToMany(Relation $relation, Builder $query, Builde } if (! is_null($farAlias) && $farAlias != $relation->getRelated()->getTable()) { - $query->from($relation->getRelated()->getTable() . ' as ' . $farAlias); + $query->from($relation->getRelated()->getTable().' as '.$farAlias); $relation->getRelated()->setTable($farAlias); } if (! is_null($pivotAlias) && $pivotAlias != $relation->getTable()) { - $table = $relation->getTable() . ' as ' . $pivotAlias; + $table = $relation->getTable().' as '.$pivotAlias; $on = $pivotAlias; } else { $table = $on = $relation->getTable(); } - $query = $query ?: $relation->getQuery(); - $query->join($table, function ($join) use ($relation, $on) { - $join->on($on . '.' . $relation->getForeignPivotKeyName(), '=', $relation->getQualifiedParentKeyName()); + $join->on($on.'.'.$relation->getForeignPivotKeyName(), '=', $relation->getQualifiedParentKeyName()); - $join->where($on . '.' . $relation->getMorphType(), '=', $relation->getMorphClass()); + $join->where($on.'.'.$relation->getMorphType(), '=', $relation->getMorphClass()); }, null, null, $type); // When a belongs to many relation uses an eloquent model to define the pivot @@ -328,8 +303,12 @@ protected static function morphToMany(Relation $relation, Builder $query, Builde ); } - return $query->whereColumn( - $relation->getRelated()->qualifyColumn($relation->getRelatedKeyName()), '=', $on . '.' . $relation->getRelatedPivotKeyName() + $query->whereColumn( + $relation->getRelated()->qualifyColumn($relation->getRelatedKeyName()), + '=', + $on.'.'.$relation->getRelatedPivotKeyName() ); + + return $query; } } diff --git a/stubs/EloquentBuilder.stub b/stubs/EloquentBuilder.stub new file mode 100644 index 0000000..81edaba --- /dev/null +++ b/stubs/EloquentBuilder.stub @@ -0,0 +1,39 @@ +joinRelation('posts', [ - function ($join) { $join->where('posts.active', '=', true); } + function ($join) { + $join->where('posts.active', '=', true); + }, ]); $this->assertEquals('select * from "users" inner join "posts" on "posts"."user_id" = "users"."id" and "posts"."active" = ?', $builder->toSql()); @@ -63,14 +68,19 @@ function ($join) { $join->where('posts.active', '=', true); } /** * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_sequential(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts.comments', [ - function ($join) { $join->where('posts.active', '=', true); }, - function ($join) { $join->where('comments.likes', '>=', 10); } + function ($join) { + $join->where('posts.active', '=', true); + }, + function ($join) { + $join->where('comments.likes', '>=', 10); + }, ]); $this->assertEquals('select * from "users" inner join "posts" on "posts"."user_id" = "users"."id" and "posts"."active" = ? inner join "comments" on "comments"."post_id" = "posts"."id" and "comments"."likes" >= ?', $builder->toSql()); @@ -80,14 +90,41 @@ function ($join) { $join->where('comments.likes', '>=', 10); } /** * @test + * + * @dataProvider queryDataProvider + */ + public function multiconstraint_leftJoinRelation(Closure $query, string $builderClass) + { + $builder = $query(new EloquentUserModelStub) + ->leftJoinRelation('posts.comments', [ + function ($join) { + $join->where('posts.active', '=', true); + }, + function ($join) { + $join->where('comments.likes', '>=', 10); + }, + ]); + + $this->assertEquals('select * from "users" left join "posts" on "posts"."user_id" = "users"."id" and "posts"."active" = ? left join "comments" on "comments"."post_id" = "posts"."id" and "comments"."likes" >= ?', $builder->toSql()); + $this->assertEquals([true, 10], $builder->getBindings()); + $this->assertEquals($builderClass, get_class($builder)); + } + + /** + * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_associative(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts.comments', [ - 'comments' => function ($join) { $join->where('comments.likes', '>=', 10); }, - 'posts' => function ($join) { $join->where('posts.active', '=', true); } + 'comments' => function ($join) { + $join->where('comments.likes', '>=', 10); + }, + 'posts' => function ($join) { + $join->where('posts.active', '=', true); + }, ]); $this->assertEquals('select * from "users" inner join "posts" on "posts"."user_id" = "users"."id" and "posts"."active" = ? inner join "comments" on "comments"."post_id" = "posts"."id" and "comments"."likes" >= ?', $builder->toSql()); @@ -97,14 +134,19 @@ public function multiconstraint_associative(Closure $query, string $builderClass /** * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_alias(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts as articles.comments as threads', [ - 'comments as threads' => function ($join) { $join->where('threads.likes', '>=', 10); }, - 'posts as articles' => function ($join) { $join->where('articles.active', '=', true); } + 'comments as threads' => function ($join) { + $join->where('threads.likes', '>=', 10); + }, + 'posts as articles' => function ($join) { + $join->where('articles.active', '=', true); + }, ]); $this->assertEquals('select * from "users" inner join "posts" as "articles" on "articles"."user_id" = "users"."id" and "articles"."active" = ? inner join "comments" as "threads" on "threads"."post_id" = "articles"."id" and "threads"."likes" >= ?', $builder->toSql()); @@ -114,13 +156,16 @@ public function multiconstraint_alias(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_single_first(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts.comments', [ - 'posts' => function ($join) { $join->where('posts.active', '=', true); } + 'posts' => function ($join) { + $join->where('posts.active', '=', true); + }, ]); $this->assertEquals('select * from "users" inner join "posts" on "posts"."user_id" = "users"."id" and "posts"."active" = ? inner join "comments" on "comments"."post_id" = "posts"."id"', $builder->toSql()); @@ -130,13 +175,16 @@ public function multiconstraint_single_first(Closure $query, string $builderClas /** * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_single_last(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts.comments', [ - 'comments' => function ($join) { $join->where('comments.likes', '>=', 10); } + 'comments' => function ($join) { + $join->where('comments.likes', '>=', 10); + }, ]); $this->assertEquals('select * from "users" inner join "posts" on "posts"."user_id" = "users"."id" inner join "comments" on "comments"."post_id" = "posts"."id" and "comments"."likes" >= ?', $builder->toSql()); @@ -146,13 +194,16 @@ public function multiconstraint_single_last(Closure $query, string $builderClass /** * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_single_middle(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts.comments.likes', [ - 'comments' => function ($join) { $join->where('comments.likes', '>=', 10); } + 'comments' => function ($join) { + $join->where('comments.likes', '>=', 10); + }, ]); $this->assertEquals('select * from "users" inner join "posts" on "posts"."user_id" = "users"."id" inner join "comments" on "comments"."post_id" = "posts"."id" and "comments"."likes" >= ? inner join "likes" on "likes"."comment_id" = "comments"."id"', $builder->toSql()); @@ -162,15 +213,20 @@ public function multiconstraint_single_middle(Closure $query, string $builderCla /** * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_skip_middle_sequential(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts.comments.likes', [ - function ($join) { $join->where('posts.active', '=', true); }, + function ($join) { + $join->where('posts.active', '=', true); + }, null, - function ($join) { $join->where('likes.emoji', '=', 'thumbs-up'); } + function ($join) { + $join->where('likes.emoji', '=', 'thumbs-up'); + }, ]); $this->assertEquals('select * from "users" inner join "posts" on "posts"."user_id" = "users"."id" and "posts"."active" = ? inner join "comments" on "comments"."post_id" = "posts"."id" inner join "likes" on "likes"."comment_id" = "comments"."id" and "likes"."emoji" = ?', $builder->toSql()); @@ -180,14 +236,19 @@ function ($join) { $join->where('likes.emoji', '=', 'thumbs-up'); } /** * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_skip_middle_associative(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts.comments.likes', [ - 'posts' => function ($join) { $join->where('posts.active', '=', true); }, - 'likes' => function ($join) { $join->where('likes.emoji', '=', 'thumbs-up'); } + 'posts' => function ($join) { + $join->where('posts.active', '=', true); + }, + 'likes' => function ($join) { + $join->where('likes.emoji', '=', 'thumbs-up'); + }, ]); $this->assertEquals('select * from "users" inner join "posts" on "posts"."user_id" = "users"."id" and "posts"."active" = ? inner join "comments" on "comments"."post_id" = "posts"."id" inner join "likes" on "likes"."comment_id" = "comments"."id" and "likes"."emoji" = ?', $builder->toSql()); @@ -197,14 +258,19 @@ public function multiconstraint_skip_middle_associative(Closure $query, string $ /** * @test + * * @dataProvider queryDataProvider */ public function multiconstraint_mix_type(Closure $query, string $builderClass) { $builder = $query(new EloquentUserModelStub) ->joinRelation('posts.comments.likes', [ - 'posts' => function ($join) { $join->type = 'left'; }, - 'likes' => function ($join) { $join->type = 'right'; } + 'posts' => function ($join) { + $join->type = 'left'; + }, + 'likes' => function ($join) { + $join->type = 'right'; + }, ]); $this->assertEquals('select * from "users" left join "posts" on "posts"."user_id" = "users"."id" inner join "comments" on "comments"."post_id" = "posts"."id" right join "likes" on "likes"."comment_id" = "comments"."id"', $builder->toSql()); diff --git a/tests/Unit/MorphManyTest.php b/tests/Unit/MorphManyTest.php index 9c3188b..140494e 100644 --- a/tests/Unit/MorphManyTest.php +++ b/tests/Unit/MorphManyTest.php @@ -9,6 +9,7 @@ class MorphManyTest extends TestCase { /** * @test + * * @dataProvider queryDataProvider */ public function basic(Closure $query, string $builderClass) @@ -23,6 +24,7 @@ public function basic(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function alias(Closure $query, string $builderClass) @@ -37,6 +39,7 @@ public function alias(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function leftJoin(Closure $query, string $builderClass) diff --git a/tests/Unit/MorphOneTest.php b/tests/Unit/MorphOneTest.php index bbd5bb7..1ba67ab 100644 --- a/tests/Unit/MorphOneTest.php +++ b/tests/Unit/MorphOneTest.php @@ -9,6 +9,7 @@ class MorphOneTest extends TestCase { /** * @test + * * @dataProvider queryDataProvider */ public function basic(Closure $query, string $builderClass) @@ -23,6 +24,7 @@ public function basic(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function alias_not_nested(Closure $query, string $builderClass) @@ -37,6 +39,7 @@ public function alias_not_nested(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function alias_nested(Closure $query, string $builderClass) @@ -51,6 +54,7 @@ public function alias_nested(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function leftJoin(Closure $query, string $builderClass) diff --git a/tests/Unit/MorphToManyTest.php b/tests/Unit/MorphToManyTest.php index e50378f..867e2a2 100644 --- a/tests/Unit/MorphToManyTest.php +++ b/tests/Unit/MorphToManyTest.php @@ -9,6 +9,7 @@ class MorphToManyTest extends TestCase { /** * @test + * * @dataProvider queryDataProvider */ public function basic(Closure $query, string $builderClass) @@ -23,6 +24,7 @@ public function basic(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function alias_far(Closure $query, string $builderClass) @@ -37,6 +39,7 @@ public function alias_far(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function alias_pivot(Closure $query, string $builderClass) @@ -51,6 +54,7 @@ public function alias_pivot(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function leftJoin(Closure $query, string $builderClass) @@ -65,6 +69,7 @@ public function leftJoin(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function constraints(Closure $query, string $builderClass) @@ -81,6 +86,7 @@ public function constraints(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function constraints_pivot(Closure $query, string $builderClass) @@ -97,6 +103,7 @@ public function constraints_pivot(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function constraints_pivot_model(Closure $query, string $builderClass) @@ -113,6 +120,7 @@ public function constraints_pivot_model(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function constraints_pivot_model_scope(Closure $query, string $builderClass) @@ -129,6 +137,7 @@ public function constraints_pivot_model_scope(Closure $query, string $builderCla /** * @test + * * @dataProvider queryDataProvider */ public function constraints_pivot_model_softDeletes(Closure $query, string $builderClass) @@ -143,6 +152,7 @@ public function constraints_pivot_model_softDeletes(Closure $query, string $buil /** * @test + * * @dataProvider queryDataProvider */ public function constraints_pivot_model_softDeletes_withTrashed(Closure $query, string $builderClass) diff --git a/tests/Unit/MorphToTest.php b/tests/Unit/MorphToTest.php index d86062c..2ebaf6a 100644 --- a/tests/Unit/MorphToTest.php +++ b/tests/Unit/MorphToTest.php @@ -3,7 +3,6 @@ namespace Reedware\LaravelRelationJoins\Tests\Unit; use Closure; -use Illuminate\Database\Eloquent\Model; use Reedware\LaravelRelationJoins\Tests\Models\EloquentFileModelStub; use Reedware\LaravelRelationJoins\Tests\Models\EloquentImageModelStub; use Reedware\LaravelRelationJoins\Tests\Models\EloquentPostModelStub; @@ -15,9 +14,6 @@ class MorphToTest extends TestCase /** * Mocks the specified select query. * - * @param string $sql - * @param array $bindings - * @param array $results * * @return void */ @@ -32,11 +28,6 @@ protected function mockSelect(string $sql, array $bindings, array $results) /** * Mocks the morph selection used by {@see $query->joinMorphRelation()}. * - * @param string $model - * @param string $relation - * @param array $results - * @param string|null $where - * @param array $bindings * * @return void */ @@ -47,7 +38,7 @@ protected function mockMorphSelect(string $model, string $relation, array $resul $column = $model->{$relation}()->getMorphType(); $this->mockSelect( - "select distinct \"{$column}\" from \"{$table}\"" . ($where ? ' where ' . $where : ''), + "select distinct \"{$column}\" from \"{$table}\"".($where ? ' where '.$where : ''), $bindings, array_map(function ($result) use ($column) { return [$column => $result]; @@ -57,12 +48,13 @@ protected function mockMorphSelect(string $model, string $relation, array $resul /** * @test + * * @dataProvider queryDataProvider */ public function basic(Closure $query, string $builderClass) { $this->mockMorphSelect(EloquentImageModelStub::class, 'imageable', [ - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $builder = $query(new EloquentImageModelStub) @@ -75,12 +67,13 @@ public function basic(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function basic_alias(Closure $query, string $builderClass) { $this->mockMorphSelect(EloquentImageModelStub::class, 'imageable', [ - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $builder = $query(new EloquentImageModelStub) @@ -93,6 +86,7 @@ public function basic_alias(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function withMorphType(Closure $query, string $builderClass) @@ -107,6 +101,7 @@ public function withMorphType(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function withMorphType_alias(Closure $query, string $builderClass) @@ -121,6 +116,7 @@ public function withMorphType_alias(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function withMorphTypes(Closure $query, string $builderClass) @@ -128,7 +124,7 @@ public function withMorphTypes(Closure $query, string $builderClass) $builder = $query(new EloquentFileModelStub) ->joinMorphRelation('link.imageable', [ EloquentImageModelStub::class, - EloquentUserModelStub::class + EloquentUserModelStub::class, ]); $this->assertEquals('select * from "files" inner join "images" on "images"."id" = "files"."link_id" and "files"."link_type" = ? inner join "users" on "users"."id" = "images"."imageable_id" and "images"."imageable_type" = ?', $builder->toSql()); @@ -138,6 +134,7 @@ public function withMorphTypes(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function withMorphTypes_normal_in(Closure $query, string $builderClass) @@ -145,7 +142,7 @@ public function withMorphTypes_normal_in(Closure $query, string $builderClass) $builder = $query(new EloquentUserModelStub) ->joinMorphRelation('uploadedFiles.link.imageable', [ EloquentImageModelStub::class, - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $this->assertEquals('select * from "users" inner join "files" on "files"."uploaded_by_id" = "users"."id" inner join "images" on "images"."id" = "files"."link_id" and "files"."link_type" = ? inner join "posts" on "posts"."id" = "images"."imageable_id" and "images"."imageable_type" = ?', $builder->toSql()); @@ -155,6 +152,7 @@ public function withMorphTypes_normal_in(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function withMorphTypes_normal_between(Closure $query, string $builderClass) @@ -162,7 +160,7 @@ public function withMorphTypes_normal_between(Closure $query, string $builderCla $builder = $query(new EloquentFileModelStub) ->joinMorphRelation('link.uploadedImages.imageable', [ EloquentUserModelStub::class, - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $this->assertEquals('select * from "files" inner join "users" on "users"."id" = "files"."link_id" and "files"."link_type" = ? inner join "images" on "images"."uploaded_by_id" = "users"."id" inner join "posts" on "posts"."id" = "images"."imageable_id" and "images"."imageable_type" = ?', $builder->toSql()); @@ -172,6 +170,7 @@ public function withMorphTypes_normal_between(Closure $query, string $builderCla /** * @test + * * @dataProvider queryDataProvider */ public function asBelongsTo(Closure $query, string $builderClass) @@ -186,6 +185,7 @@ public function asBelongsTo(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function multitype(Closure $query, string $builderClass) @@ -204,12 +204,13 @@ public function multitype(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function nested_in(Closure $query, string $builderClass) { $this->mockMorphSelect(EloquentImageModelStub::class, 'imageable', [ - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $builder = $query(new EloquentUserModelStub) @@ -222,13 +223,14 @@ public function nested_in(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function nested_out(Closure $query, string $builderClass) { $builder = $query(new EloquentImageModelStub) ->joinMorphRelation('imageable.comments', [ - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $this->assertEquals('select * from "images" inner join "posts" on "posts"."id" = "images"."imageable_id" and "images"."imageable_type" = ? inner join "comments" on "comments"."post_id" = "posts"."id"', $builder->toSql()); @@ -238,6 +240,7 @@ public function nested_out(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function leftJoin(Closure $query, string $builderClass) @@ -252,6 +255,7 @@ public function leftJoin(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function rightJoin(Closure $query, string $builderClass) @@ -266,6 +270,7 @@ public function rightJoin(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function crossJoin(Closure $query, string $builderClass) @@ -280,6 +285,7 @@ public function crossJoin(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function joinThrough_in(Closure $query, string $builderClass) @@ -287,7 +293,7 @@ public function joinThrough_in(Closure $query, string $builderClass) $builder = $query(new EloquentUserModelStub) ->joinRelation('uploadedImages') ->joinThroughMorphRelation('uploadedImages.imageable', [ - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $this->assertEquals('select * from "users" inner join "images" on "images"."uploaded_by_id" = "users"."id" inner join "posts" on "posts"."id" = "images"."imageable_id" and "images"."imageable_type" = ?', $builder->toSql()); @@ -297,6 +303,7 @@ public function joinThrough_in(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function joinThrough_out(Closure $query, string $builderClass) @@ -312,6 +319,7 @@ public function joinThrough_out(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function leftJoinThrough(Closure $query, string $builderClass) @@ -319,7 +327,7 @@ public function leftJoinThrough(Closure $query, string $builderClass) $builder = $query(new EloquentUserModelStub) ->joinRelation('uploadedImages') ->leftJoinThroughMorphRelation('uploadedImages.imageable', [ - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $this->assertEquals('select * from "users" inner join "images" on "images"."uploaded_by_id" = "users"."id" left join "posts" on "posts"."id" = "images"."imageable_id" and "images"."imageable_type" = ?', $builder->toSql()); @@ -329,6 +337,7 @@ public function leftJoinThrough(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function rightJoinThrough(Closure $query, string $builderClass) @@ -336,7 +345,7 @@ public function rightJoinThrough(Closure $query, string $builderClass) $builder = $query(new EloquentUserModelStub) ->joinRelation('uploadedImages') ->rightJoinThroughMorphRelation('uploadedImages.imageable', [ - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $this->assertEquals('select * from "users" inner join "images" on "images"."uploaded_by_id" = "users"."id" right join "posts" on "posts"."id" = "images"."imageable_id" and "images"."imageable_type" = ?', $builder->toSql()); @@ -346,6 +355,7 @@ public function rightJoinThrough(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function crossJoinThrough(Closure $query, string $builderClass) @@ -353,7 +363,7 @@ public function crossJoinThrough(Closure $query, string $builderClass) $builder = $query(new EloquentUserModelStub) ->joinRelation('uploadedImages') ->crossJoinThroughMorphRelation('uploadedImages.imageable', [ - EloquentPostModelStub::class + EloquentPostModelStub::class, ]); $this->assertEquals('select * from "users" inner join "images" on "images"."uploaded_by_id" = "users"."id" cross join "posts" on "posts"."id" = "images"."imageable_id" and "images"."imageable_type" = ?', $builder->toSql()); diff --git a/tests/Unit/MorphedByManyTest.php b/tests/Unit/MorphedByManyTest.php index 788a4b8..0c96ec7 100644 --- a/tests/Unit/MorphedByManyTest.php +++ b/tests/Unit/MorphedByManyTest.php @@ -10,6 +10,7 @@ class MorphedByManyTest extends TestCase { /** * @test + * * @dataProvider queryDataProvider */ public function basic(Closure $query, string $builderClass) @@ -24,6 +25,7 @@ public function basic(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function alias(Closure $query, string $builderClass) @@ -38,6 +40,7 @@ public function alias(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function circular(Closure $query, string $builderClass) @@ -52,6 +55,7 @@ public function circular(Closure $query, string $builderClass) /** * @test + * * @dataProvider queryDataProvider */ public function leftJoin(Closure $query, string $builderClass) diff --git a/tests/Unit/RelationJoinQueryTest.php b/tests/Unit/RelationJoinQueryTest.php index 21ccb42..4a9b7ca 100644 --- a/tests/Unit/RelationJoinQueryTest.php +++ b/tests/Unit/RelationJoinQueryTest.php @@ -13,7 +13,7 @@ class RelationJoinQueryTest extends TestCase public function nonRelation() { $this->expectException(LogicException::class); - $this->expectExceptionMessage(EloquentUserModelStub::class . '::active must return a relationship instance.'); + $this->expectExceptionMessage(EloquentUserModelStub::class.'::active must return a relationship instance.'); $builder = (new EloquentUserModelStub) ->useCustomBuilder(false) @@ -24,7 +24,7 @@ public function nonRelation() public function unsupportedRelation() { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Unsupported relation type [' . CustomRelation::class . '].'); + $this->expectExceptionMessage('Unsupported relation type ['.CustomRelation::class.'].'); $builder = (new EloquentUserModelStub) ->useCustomBuilder(false) diff --git a/tests/Unit/TestCase.php b/tests/Unit/TestCase.php index c5f7aa0..2ad6616 100644 --- a/tests/Unit/TestCase.php +++ b/tests/Unit/TestCase.php @@ -26,8 +26,6 @@ class TestCase extends TestBase /** * Prepares the test for execution. - * - * @return void */ protected function setUp(): void { @@ -39,8 +37,6 @@ protected function setUp(): void /** * Mocks the connection resolver for testing. - * - * @return void */ protected function setUpConnectionResolver(): void { @@ -74,8 +70,6 @@ protected function setUpConnectionResolver(): void /** * Registers the package service provider. - * - * @return void */ protected function registerServiceProvider(): void { @@ -88,8 +82,6 @@ protected function registerServiceProvider(): void /** * Cleans up after the test has been exected. - * - * @return void */ protected function tearDown(): void { @@ -113,7 +105,7 @@ public function queryDataProvider() return [ 'Eloquent Builder' => [$newQuery, EloquentBuilder::class], - 'Custom Builder' => [$customQuery, CustomBuilder::class] + 'Custom Builder' => [$customQuery, CustomBuilder::class], ]; } } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 86f9239..bc39414 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -12,7 +12,7 @@ | */ -require __DIR__ . '/../vendor/autoload.php'; +require __DIR__.'/../vendor/autoload.php'; use Illuminate\Support\Carbon;