Compare commits

..
3 Commits
Author SHA1 Message Date
Barry vd. Heuvel f4d110d7a8 Fix baseline 2026-02-07 14:29:43 +01:00
Barry vd. Heuvel 5af7ebdec7 Fix cs 2026-02-07 14:27:39 +01:00
Barry vd. Heuvel fe65d97001 Use larastan instead of psalm 2026-02-07 14:24:53 +01:00
36 changed files with 165 additions and 976 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Git checkout - name: Git checkout
uses: actions/checkout@v7 uses: actions/checkout@v6
- name: Validate Composer configuration - name: Validate Composer configuration
run: composer validate --strict run: composer validate --strict
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v7 uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
# echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV
# Drafts your next Release notes as Pull Requests are merged into "master" # Drafts your next Release notes as Pull Requests are merged into "master"
- uses: release-drafter/release-drafter@v7 - uses: release-drafter/release-drafter@v6
# (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
# with: # with:
# config-name: my-config.yml # config-name: my-config.yml
+61 -65
View File
@@ -1,79 +1,75 @@
name: Integration Tests name: Integration Tests
on: on:
push: push:
branches: branches:
- master - master
pull_request: pull_request:
branches: branches:
- "*" - "*"
schedule:
- cron: '0 0 * * *'
jobs: jobs:
php-laravel-integration-tests: php-laravel-integration-tests:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
timeout-minutes: 15
env:
COMPOSER_NO_INTERACTION: 1
strategy:
fail-fast: false
matrix:
php: [8.4, 8.3, 8.2]
laravel: [11.x, 12.x]
timeout-minutes: 15 name: P${{ matrix.php }} - Laravel${{ matrix.laravel }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
path: src
env: - name: Setup PHP
COMPOSER_NO_INTERACTION: 1 uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
strategy: - name: Install dependencies
fail-fast: false run: |
matrix: composer create-project --prefer-dist laravel/laravel:${{ matrix.laravel }} --stability=dev --no-progress sample
php: [8.5, 8.4, 8.3, 8.2] cd sample
laravel: [12.x, 13.x] composer config minimum-stability dev
exclude: composer update --prefer-stable --prefer-dist --no-progress
- laravel: 13.x
php: 8.2
name: P${{ matrix.php }} - Laravel${{ matrix.laravel }} - name: Add package from source
run: |
cd sample
sed -e 's|"type": "project",|&\n"repositories": [ { "type": "path", "url": "../src" } ],|' -i composer.json
composer require --dev "barryvdh/laravel-ide-helper:*" --with-all-dependencies
steps: - name: Execute generate run
- name: Checkout code run: |
uses: actions/checkout@v7 cd sample
with: php artisan ide-helper:generate
path: src
- name: Setup PHP - name: Execute meta run
uses: shivammathur/setup-php@v2 run: |
with: cd sample
php-version: ${{ matrix.php }} php artisan ide-helper:meta -v
coverage: none
- name: Install dependencies - name: Check file existence
run: | run: |
composer create-project --prefer-dist laravel/laravel:${{ matrix.laravel }} --stability=dev --no-progress sample ls sample/_ide_helper.php
cd sample ls sample/.phpstorm.meta.php
composer config minimum-stability dev
composer update --prefer-stable --prefer-dist --no-progress
- name: Add package from source
run: |
cd sample
sed -e 's|"type": "project",|&\n"repositories": [ { "type": "path", "url": "../src" } ],|' -i composer.json
composer require --dev "barryvdh/laravel-ide-helper:*" --with-all-dependencies
- name: Execute generate run - name: Check logs
run: | run: |
cd sample if [ `ls -1q "sample/storage/logs/" | wc -l` -gt 0 ]; then
php artisan ide-helper:generate for logfile in sample/storage/logs/*; do
echo "-- $logfile --"
- name: Execute meta run cat $logfile
run: | done
cd sample exit 1
php artisan ide-helper:meta -v fi
- name: Check file existence
run: |
ls sample/_ide_helper.php
ls sample/.phpstorm.meta.php
- name: Check logs
run: |
if [ `ls -1q "sample/storage/logs/" | wc -l` -gt 0 ]; then
for logfile in sample/storage/logs/*; do
echo "-- $logfile --"
cat $logfile
done
exit 1
fi
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
+24 -28
View File
@@ -7,13 +7,13 @@ on:
pull_request: pull_request:
branches: branches:
- "*" - "*"
schedule:
- cron: '0 0 * * *'
jobs: jobs:
php-tests: php-tests:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
timeout-minutes: 15 timeout-minutes: 15
env: env:
COMPOSER_NO_INTERACTION: 1 COMPOSER_NO_INTERACTION: 1
@@ -22,40 +22,36 @@ jobs:
matrix: matrix:
os: [ubuntu-latest] os: [ubuntu-latest]
php: [8.5, 8.4, 8.3, 8.2] php: [8.5, 8.4, 8.3, 8.2]
laravel: [12.x, 13.x, ~11.15] laravel: [~11.15, 12.x]
stability: [prefer-lowest, prefer-stable] stability: [prefer-lowest, prefer-stable]
include: include:
- os: windows-latest - os: windows-latest
php: 8.3 php: 8.3
laravel: 12.x laravel: 12.x
stability: prefer-stable stability: prefer-stable
exclude:
- laravel: 13.x
php: 8.2
steps: steps:
- name: Set git to use LF - name: Set git to use LF
if: "${{ matrix.os == 'windows-latest' }}" if: ${{ matrix.os == 'windows-latest' }}
run: | run: |
git config --global core.autocrlf false git config --global core.autocrlf false
git config --global core.eol lf git config --global core.eol lf
- name: Checkout code - name: Checkout code
uses: actions/checkout@v7 uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: ${{ matrix.php }} php-version: ${{ matrix.php }}
coverage: none coverage: none
extensions: pdo_sqlite, fileinfo extensions: pdo_sqlite, fileinfo
- name: Install dependencies - name: Install dependencies
run: | run: |
composer remove vimeo/psalm --no-update --dev composer remove vimeo/psalm --no-update --dev
composer remove friendsofphp/php-cs-fixer --no-update --dev composer remove friendsofphp/php-cs-fixer --no-update --dev
composer require "laravel/framework:${{ matrix.laravel }}" --no-update --no-progress composer require "laravel/framework:${{ matrix.laravel }}" --no-update --no-progress
composer update --prefer-dist --no-progress --${{ matrix.stability }} composer update --prefer-dist --no-progress --${{ matrix.stability }}
- name: Execute Unit Tests - name: Execute Unit Tests
run: composer test-ci run: composer test-ci
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
ref: ${{ github.event.release.target_commitish }} ref: ${{ github.event.release.target_commitish }}
-1
View File
@@ -1,5 +1,4 @@
.phpunit.result.cache .phpunit.result.cache
/auth.json
/build /build
/.idea /.idea
/.php-cs-fixer.cache /.php-cs-fixer.cache
-33
View File
@@ -1,38 +1,5 @@
# Changelog # Changelog
## v3.7.0 - 2026-03-17
### What's Changed
* Skip calling fake() when required parameters exist (Socialite compatibility) by @Jaspur in https://github.com/barryvdh/laravel-ide-helper/pull/1746
* Make Soft Deleted Relationships Nullable by @evan-burrell in https://github.com/barryvdh/laravel-ide-helper/pull/1749
* Support all DNF types from PHP RFC by @gurmanolog in https://github.com/barryvdh/laravel-ide-helper/pull/1751
* Replace psalm with larastan by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1755
* perf: reduce redundant lookups and file I/O in generation hot paths by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1757
* fix: wrap bare intersection types in parentheses when adding nullable by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1756
* feat(ModelsCommand): add configuration option to disable model query methods by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1692
* ci: add PHP 8.5 to integration test matrix by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1760
* chore: update test snapshots after merge order issue by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1762
* Laravel 13.x Compatibility by @laravel-shift in https://github.com/barryvdh/laravel-ide-helper/pull/1763
* fix: Correct indentation in camel case config comment block by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1767
* fix: Place PHPDoc before class attributes when writing to models by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1765
* fix: Don't extend root's parent class for facade stubs in helper file by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1766
* Fix PHPDoc placement before PHP 8 class attributes and add regression tests by @Copilot in https://github.com/barryvdh/laravel-ide-helper/pull/1769
* fix: Skip autoload exception when class existence is being checked by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1764
* Remove closure in attribute test by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1772
* Bump branch alias to 3.6 by @jnoordsij in https://github.com/barryvdh/laravel-ide-helper/pull/1774
### New Contributors
* @Jaspur made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1746
* @evan-burrell made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1749
* @gurmanolog made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1751
* @laravel-shift made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1763
* @isaackaara made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1767
* @Copilot made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1769
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.6.1...v3.7.0
## v3.6.1 - 2025-12-10 ## v3.6.1 - 2025-12-10
### What's Changed ### What's Changed
+11 -11
View File
@@ -25,21 +25,21 @@
"ext-json": "*", "ext-json": "*",
"barryvdh/reflection-docblock": "^2.4", "barryvdh/reflection-docblock": "^2.4",
"composer/class-map-generator": "^1.0", "composer/class-map-generator": "^1.0",
"illuminate/console": "^11.15 || ^12 || ^13.0", "illuminate/console": "^11.15 || ^12",
"illuminate/database": "^11.15 || ^12 || ^13.0", "illuminate/database": "^11.15 || ^12",
"illuminate/filesystem": "^11.15 || ^12 || ^13.0", "illuminate/filesystem": "^11.15 || ^12",
"illuminate/support": "^11.15 || ^12 || ^13.0" "illuminate/support": "^11.15 || ^12"
}, },
"require-dev": { "require-dev": {
"ext-pdo_sqlite": "*", "ext-pdo_sqlite": "*",
"friendsofphp/php-cs-fixer": "^3", "friendsofphp/php-cs-fixer": "^3",
"illuminate/config": "^11.15 || ^12 || ^13.0", "illuminate/config": "^11.15 || ^12",
"illuminate/view": "^11.15 || ^12 || ^13.0", "illuminate/view": "^11.15 || ^12",
"larastan/larastan": "^3.1", "larastan/larastan": "^3.1",
"mockery/mockery": "^1.4", "mockery/mockery": "^1.4",
"orchestra/testbench": "^9.2 || ^10 || ^11.0", "orchestra/testbench": "^9.2 || ^10",
"phpstan/phpstan-phpunit": "^2.0", "phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.12", "phpunit/phpunit": "^10.5 || ^11.5.3",
"spatie/phpunit-snapshot-assertions": "^4 || ^5", "spatie/phpunit-snapshot-assertions": "^4 || ^5",
"vlucas/phpdotenv": "^5" "vlucas/phpdotenv": "^5"
}, },
@@ -66,7 +66,7 @@
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.7-dev" "dev-master": "3.5-dev"
}, },
"laravel": { "laravel": {
"providers": [ "providers": [
@@ -76,7 +76,6 @@
}, },
"scripts": { "scripts": {
"analyze": "phpstan", "analyze": "phpstan",
"analyze-set-baseline": "phpstan --generate-baseline",
"check-style": [ "check-style": [
"php-cs-fixer fix --diff --diff-format=udiff --dry-run", "php-cs-fixer fix --diff --diff-format=udiff --dry-run",
"php-cs-fixer fix --diff --diff-format=udiff --dry-run --config=.php_cs.tests.php" "php-cs-fixer fix --diff --diff-format=udiff --dry-run --config=.php_cs.tests.php"
@@ -85,8 +84,9 @@
"php-cs-fixer fix", "php-cs-fixer fix",
"php-cs-fixer fix --config=.php-cs-fixer.tests.php" "php-cs-fixer fix --config=.php-cs-fixer.tests.php"
], ],
"analyze-set-baseline": "phpstan --generate-baseline",
"test": "phpunit", "test": "phpunit",
"test-ci": "phpunit", "test-ci": "phpunit -d --without-creating-snapshots",
"test-regenerate": "phpunit -d --update-snapshots" "test-regenerate": "phpunit -d --update-snapshots"
} }
} }
+23 -34
View File
@@ -47,17 +47,6 @@ return [
'include_fluent' => false, 'include_fluent' => false,
/*
|--------------------------------------------------------------------------
| Write model query methods
|--------------------------------------------------------------------------
|
| Set to false to disable generated docs for the 'query()', 'newQuery()' and 'newModelQuery()' methods.
|
*/
'write_query_methods' => true,
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Write model magic methods | Write model magic methods
@@ -201,29 +190,29 @@ return [
], ],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Support for camel cased models | Support for camel cased models
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| |
| There are some Laravel packages (such as Eloquence) that allow for accessing | There are some Laravel packages (such as Eloquence) that allow for accessing
| Eloquent model properties via camel case, instead of snake case. | Eloquent model properties via camel case, instead of snake case.
| |
| Enabling this option will support these packages by saving all model | Enabling this option will support these packages by saving all model
| properties as camel case, instead of snake case. | properties as camel case, instead of snake case.
| |
| For example, normally you would see this: | For example, normally you would see this:
| |
| * @property \Illuminate\Support\Carbon $created_at | * @property \Illuminate\Support\Carbon $created_at
| * @property \Illuminate\Support\Carbon $updated_at | * @property \Illuminate\Support\Carbon $updated_at
| |
| With this enabled, the properties will be this: | With this enabled, the properties will be this:
| |
| * @property \Illuminate\Support\Carbon $createdAt | * @property \Illuminate\Support\Carbon $createdAt
| * @property \Illuminate\Support\Carbon $updatedAt | * @property \Illuminate\Support\Carbon $updatedAt
| |
| Note, it is currently an all-or-nothing option. | Note, it is currently an all-or-nothing option.
| |
*/ */
'model_camel_case_properties' => false, 'model_camel_case_properties' => false,
/* /*
+12 -18
View File
@@ -101,12 +101,7 @@ class Alias
} }
if ($facade === '\Illuminate\Database\Eloquent\Model') { if ($facade === '\Illuminate\Database\Eloquent\Model') {
$this->usedMethods = [ $this->usedMethods = ['decrement', 'increment'];
'decrement' => true,
'decrementEach' => true,
'increment' => true,
'incrementEach' => true,
];
} }
} }
@@ -192,7 +187,7 @@ class Alias
public function shouldExtendParentClass() public function shouldExtendParentClass()
{ {
return $this->parentClass return $this->parentClass
&& !is_subclass_of($this->extends, Facade::class); && $this->getExtendsNamespace() !== '\\Illuminate\\Support\\Facades';
} }
/** /**
@@ -386,7 +381,7 @@ class Alias
$method = new \ReflectionMethod($className, $name); $method = new \ReflectionMethod($className, $name);
$class = new ReflectionClass($className); $class = new ReflectionClass($className);
if (!isset($this->usedMethods[$magic])) { if (!in_array($magic, $this->usedMethods)) {
if ($class !== $this->root) { if ($class !== $this->root) {
$this->methods[] = new Method( $this->methods[] = new Method(
$method, $method,
@@ -398,7 +393,7 @@ class Alias
$this->getTemplateNames() $this->getTemplateNames()
); );
} }
$this->usedMethods[$magic] = true; $this->usedMethods[] = $magic;
} }
} }
} }
@@ -415,7 +410,7 @@ class Alias
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC); $methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
if ($methods) { if ($methods) {
foreach ($methods as $method) { foreach ($methods as $method) {
if (!isset($this->usedMethods[$method->name])) { if (!in_array($method->name, $this->usedMethods)) {
// Only add the methods to the output when the root is not the same as the class. // Only add the methods to the output when the root is not the same as the class.
// And don't add the __*() methods // And don't add the __*() methods
if ($this->extends !== $class && substr($method->name, 0, 2) !== '__') { if ($this->extends !== $class && substr($method->name, 0, 2) !== '__') {
@@ -429,7 +424,7 @@ class Alias
$this->getTemplateNames(), $this->getTemplateNames(),
); );
} }
$this->usedMethods[$method->name] = true; $this->usedMethods[] = $method->name;
} }
} }
} }
@@ -440,7 +435,7 @@ class Alias
$properties = $reflection->getStaticProperties(); $properties = $reflection->getStaticProperties();
$macros = isset($properties['macros']) ? $properties['macros'] : []; $macros = isset($properties['macros']) ? $properties['macros'] : [];
foreach ($macros as $macro_name => $macro_func) { foreach ($macros as $macro_name => $macro_func) {
if (!isset($this->usedMethods[$macro_name])) { if (!in_array($macro_name, $this->usedMethods)) {
try { try {
$method = $this->getMacroFunction($macro_func); $method = $this->getMacroFunction($macro_func);
} catch (Throwable $e) { } catch (Throwable $e) {
@@ -456,7 +451,7 @@ class Alias
$this->classAliases, $this->classAliases,
$this->getReturnTypeNormalizers($reflection) $this->getReturnTypeNormalizers($reflection)
); );
$this->usedMethods[$macro_name] = true; $this->usedMethods[] = $macro_name;
} }
} }
} }
@@ -594,13 +589,12 @@ class Alias
*/ */
protected function removeDuplicateMethodsFromPhpDoc() protected function removeDuplicateMethodsFromPhpDoc()
{ {
$methodNames = []; $methodNames = array_map(function (Method $method) {
foreach ($this->getMethods() as $method) { return $method->getName();
$methodNames[$method->getName()] = true; }, $this->getMethods());
}
foreach ($this->phpdoc->getTags() as $tag) { foreach ($this->phpdoc->getTags() as $tag) {
if ($tag instanceof MethodTag && isset($methodNames[$tag->getMethodName()])) { if ($tag instanceof MethodTag && in_array($tag->getMethodName(), $methodNames)) {
$this->phpdoc->deleteTag($tag); $this->phpdoc->deleteTag($tag);
} }
} }
-12
View File
@@ -195,18 +195,6 @@ class MetaCommand extends Command
return; return;
} }
// Don't throw when class existence is being checked via class_exists(),
// interface_exists(), trait_exists(), or enum_exists(). These functions
// expect the autoloader to return gracefully when the class doesn't exist.
// Throwing here would break libraries that use class_exists() to check for
// optional dependencies (e.g. Doctrine ORM checking for removed classes).
$existsFunctions = ['class_exists', 'interface_exists', 'trait_exists', 'enum_exists'];
foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3) as $frame) {
if (isset($frame['function']) && in_array($frame['function'], $existsFunctions, true)) {
return;
}
}
throw new \ReflectionException("Class '$class' not found."); throw new \ReflectionException("Class '$class' not found.");
}; };
+26 -132
View File
@@ -44,7 +44,6 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Database\Schema\Builder; use Illuminate\Database\Schema\Builder;
use Illuminate\Filesystem\Filesystem; use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
@@ -124,14 +123,6 @@ class ModelsCommand extends Command
protected $reset; protected $reset;
protected $phpstorm_noinspections; protected $phpstorm_noinspections;
protected $write_model_external_builder_methods; protected $write_model_external_builder_methods;
/**
* @var array<string, \SplFileObject>
*/
protected $fileCache = [];
/**
* @var array<string, Context>
*/
protected $contextCache = [];
/** /**
* @var array<string, true> * @var array<string, true>
*/ */
@@ -535,7 +526,7 @@ class ModelsCommand extends Command
} }
if ($isNullable) { if ($isNullable) {
$type = $this->wrapIntersectionType($type) . '|null'; $type .= '|null';
} else { } else {
$type = str_replace($nullString, '', $type); $type = str_replace($nullString, '', $type);
} }
@@ -543,22 +534,6 @@ class ModelsCommand extends Command
return $type; return $type;
} }
/**
* Wraps a bare intersection type in parentheses for correct DNF syntax.
*
* For example, `A&B` becomes `(A&B)` so that adding `|null` produces
* `(A&B)|null` instead of the ambiguous `A&B|null`.
* Types that are already parenthesized or contain union types are returned as-is.
*/
protected function wrapIntersectionType(string $type): string
{
if (str_contains($type, '&') && !str_contains($type, '|') && $type[0] !== '(') {
return '(' . $type . ')';
}
return $type;
}
/** /**
* Returns the override type for the give type. * Returns the override type for the give type.
* *
@@ -721,16 +696,13 @@ class ModelsCommand extends Command
); );
$this->setMethod($name, $builder . '<static>|' . $modelName, $args, $comment); $this->setMethod($name, $builder . '<static>|' . $modelName, $args, $comment);
} }
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery']) } elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) {
) { $builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery()));
if ($this->laravel['config']->get('ide-helper.write_query_methods', true)) {
$builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery()));
$this->setMethod( $this->setMethod(
$method, $method,
$builder . '<static>|' . $this->getClassNameInDestinationFile($model, get_class($model)) $builder . '<static>|' . $this->getClassNameInDestinationFile($model, get_class($model))
); );
}
if ($this->write_model_external_builder_methods) { if ($this->write_model_external_builder_methods) {
$this->writeModelExternalBuilderMethods($model); $this->writeModelExternalBuilderMethods($model);
@@ -749,19 +721,14 @@ class ModelsCommand extends Command
$type = (string)$this->getReturnTypeFromDocBlock($reflection); $type = (string)$this->getReturnTypeFromDocBlock($reflection);
} }
$fileName = $reflection->getFileName(); $file = new \SplFileObject($reflection->getFileName());
if (!isset($this->fileCache[$fileName])) {
$this->fileCache[$fileName] = new \SplFileObject($fileName);
}
$file = $this->fileCache[$fileName];
$file->seek($reflection->getStartLine() - 1); $file->seek($reflection->getStartLine() - 1);
$lines = []; $code = '';
while ($file->key() < $reflection->getEndLine()) { while ($file->key() < $reflection->getEndLine()) {
$lines[] = $file->current(); $code .= $file->current();
$file->next(); $file->next();
} }
$code = implode('', $lines);
$code = trim(preg_replace('/\s\s+/', '', $code)); $code = trim(preg_replace('/\s\s+/', '', $code));
$begin = strpos($code, 'function('); $begin = strpos($code, 'function(');
$code = substr($code, $begin, strrpos($code, '}') - $begin + 1); $code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
@@ -944,55 +911,13 @@ class ModelsCommand extends Command
} }
} }
if ( if ($this->relatedModelUsesSoftDeletes($relationObj)) {
$this->relatedModelUsesSoftDeletes($relationObj)
&& !$this->relationIncludesNonTrashedParents($relationObj)
) {
return true; return true;
} }
return false; return false;
} }
/**
* Check whether the relation explicitly opts into returning non-soft-deleted parents
* via ->withTrashed(), in which case the SoftDeletes-based nullability no longer applies.
*
* Returns false for ->onlyTrashed() and ->withoutTrashed(), which still leave the
* relation potentially empty depending on the parent's soft-delete state.
*
* @param Relation $relationObj
*
* @return bool
*/
protected function relationIncludesNonTrashedParents(Relation $relationObj): bool
{
$query = $relationObj->getQuery();
if (!in_array(SoftDeletingScope::class, $query->removedScopes(), true)) {
return false;
}
$relatedModel = $relationObj->getRelated();
if (!method_exists($relatedModel, 'getQualifiedDeletedAtColumn')) {
return true;
}
$deletedAtColumn = $relatedModel->getQualifiedDeletedAtColumn();
foreach ($query->getQuery()->wheres ?? [] as $where) {
if (
($where['column'] ?? null) === $deletedAtColumn
&& in_array($where['type'] ?? null, ['Null', 'NotNull'], true)
) {
return false;
}
}
return true;
}
/** /**
* Check if the related model uses the SoftDeletes trait * Check if the related model uses the SoftDeletes trait
* *
@@ -1057,7 +982,7 @@ class ModelsCommand extends Command
if ($type !== null) { if ($type !== null) {
$newType = $this->getTypeOverride($type); $newType = $this->getTypeOverride($type);
if ($nullable) { if ($nullable) {
$newType = $this->wrapIntersectionType($newType) . '|null'; $newType .= '|null';
} }
$this->properties[$name]['type'] = $newType; $this->properties[$name]['type'] = $newType;
} }
@@ -1245,14 +1170,6 @@ class ModelsCommand extends Command
$replace = "{$modelDocComment}\n"; $replace = "{$modelDocComment}\n";
$pos = strpos($contents, "final class {$classname}") ?: strpos($contents, "class {$classname}"); $pos = strpos($contents, "final class {$classname}") ?: strpos($contents, "class {$classname}");
if ($pos !== false) { if ($pos !== false) {
// If PHP 8 attributes (e.g. #[ObservedBy(...)]) precede the class
// declaration, insert the docblock before the first attribute so that
// the resulting order is: docblock → attributes → class.
$before = substr($contents, 0, $pos);
if (preg_match('/((?:#\[.+?\]\s*)+)$/s', $before, $matches)) {
$pos -= strlen($matches[1]);
$replace = "{$modelDocComment}\n";
}
$contents = substr_replace($contents, $replace, $pos, 0); $contents = substr_replace($contents, $replace, $pos, 0);
} }
} }
@@ -1358,18 +1275,13 @@ class ModelsCommand extends Command
} }
} }
protected ?array $cachedRelationTypes = null;
protected ?array $cachedRelationReturnTypes = null;
/** /**
* Returns the available relation types * Returns the available relation types
*/ */
protected function getRelationTypes(): array protected function getRelationTypes(): array
{ {
return $this->cachedRelationTypes ??= array_merge( $configuredRelations = $this->laravel['config']->get('ide-helper.additional_relation_types', []);
self::RELATION_TYPES, return array_merge(self::RELATION_TYPES, $configuredRelations);
$this->laravel['config']->get('ide-helper.additional_relation_types', [])
);
} }
/** /**
@@ -1377,7 +1289,7 @@ class ModelsCommand extends Command
*/ */
protected function getRelationReturnTypes(): array protected function getRelationReturnTypes(): array
{ {
return $this->cachedRelationReturnTypes ??= $this->laravel['config']->get('ide-helper.additional_relation_return_types', []); return $this->laravel['config']->get('ide-helper.additional_relation_return_types', []);
} }
/** /**
@@ -1452,7 +1364,11 @@ class ModelsCommand extends Command
*/ */
protected function getCommentFromDocBlock(\ReflectionMethod $reflection) protected function getCommentFromDocBlock(\ReflectionMethod $reflection)
{ {
$context = $this->getDocBlockContext($reflection); $phpDocContext = (new ContextFactory())->createFromReflector($reflection);
$context = new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
);
$comment = ''; $comment = '';
$phpdoc = new DocBlock($reflection, $context); $phpdoc = new DocBlock($reflection, $context);
@@ -1473,7 +1389,11 @@ class ModelsCommand extends Command
*/ */
protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection, ?\Reflector $reflectorForContext = null) protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection, ?\Reflector $reflectorForContext = null)
{ {
$context = $this->getDocBlockContext($reflectorForContext ?? $reflection); $phpDocContext = (new ContextFactory())->createFromReflector($reflectorForContext ?? $reflection);
$context = new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
);
$type = null; $type = null;
$phpdoc = new DocBlock($reflection, $context); $phpdoc = new DocBlock($reflection, $context);
@@ -1491,31 +1411,6 @@ class ModelsCommand extends Command
return $type; return $type;
} }
protected function getDocBlockContext(\Reflector $reflector): Context
{
if ($reflector instanceof \ReflectionMethod) {
$key = $reflector->getDeclaringClass()->getName();
} elseif ($reflector instanceof ReflectionClass) {
$key = $reflector->getName();
} else {
$phpDocContext = (new ContextFactory())->createFromReflector($reflector);
return new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
);
}
if (!isset($this->contextCache[$key])) {
$phpDocContext = (new ContextFactory())->createFromReflector($reflector);
$this->contextCache[$key] = new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
);
}
return $this->contextCache[$key];
}
protected function getReturnTypeFromReflection(\ReflectionMethod $reflection): ?string protected function getReturnTypeFromReflection(\ReflectionMethod $reflection): ?string
{ {
$returnType = $reflection->getReturnType(); $returnType = $reflection->getReturnType();
@@ -1721,10 +1616,9 @@ class ModelsCommand extends Command
*/ */
protected function getUsedClassNames(ReflectionClass $reflection): array protected function getUsedClassNames(ReflectionClass $reflection): array
{ {
$context = $this->getDocBlockContext($reflection);
$namespaceAliases = array_flip(array_map(function ($alias) { $namespaceAliases = array_flip(array_map(function ($alias) {
return ltrim($alias, '\\'); return ltrim($alias, '\\');
}, $context->getNamespaceAliases())); }, (new ContextFactory())->createFromReflector($reflection)->getNamespaceAliases()));
$namespaceAliases[$reflection->getName()] = $reflection->getShortName(); $namespaceAliases[$reflection->getName()] = $reflection->getShortName();
return $namespaceAliases; return $namespaceAliases;
-46
View File
@@ -76,8 +76,6 @@ class Method
} catch (\Exception $e) { } catch (\Exception $e) {
} }
$this->removePhpDocParamTagsForOptionalParameters();
//Get the parameters, including formatted default values //Get the parameters, including formatted default values
$this->getParameters($method); $this->getParameters($method);
@@ -250,50 +248,6 @@ class Method
} }
} }
/**
* Remove @param tags for optional reflection parameters so generated stubs match arity
* checks in static analysis (optional params still appear in the PHP signature).
*/
protected function removePhpDocParamTagsForOptionalParameters(): void
{
if (!$this->method instanceof \ReflectionMethod) {
return;
}
$declaring = $this->method->getDeclaringClass()->getName();
if (
$declaring !== \Illuminate\Database\Query\Builder::class
&& $declaring !== \Illuminate\Database\Eloquent\Builder::class
) {
return;
}
foreach ($this->method->getParameters() as $param) {
if (!$param->isOptional() || $param->isVariadic()) {
continue;
}
if (!$param->isDefaultValueAvailable()) {
continue;
}
$default = $param->getDefaultValue();
// Keep @param for null/array defaults (nullable and list defaults stay documented).
// Strip only non-null scalars (e.g. $boolean = 'and', $not = false) so tools that
// count @param tags do not report false "missing argument" errors on shortened calls.
if ($default === null || is_array($default) || !is_scalar($default)) {
continue;
}
$varName = '$' . $param->getName();
$paramTags = $this->phpdoc->getTagsByName('param');
foreach (array_values($paramTags) as $tag) {
if ($tag instanceof ParamTag && $tag->getVariableName() === $varName) {
$this->phpdoc->deleteTag($tag);
}
}
}
}
/** /**
* Normalize the parameters * Normalize the parameters
* *
@@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
use Illuminate\Database\Eloquent\Model;
abstract class BaseModel extends Model
{
}
@@ -1,9 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
interface CanBeAssigned
{
}
@@ -1,31 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class MorphToIntersection extends Model
{
protected $table = 'morphs';
/** @return MorphTo<(BaseModel&CanBeAssigned), $this> */
public function assigneeWithParens(): MorphTo
{
return $this->morphTo(type: 'nullable_relation_morph_to_type', id: 'nullable_relation_morph_to_id');
}
/** @return MorphTo<BaseModel&CanBeAssigned, $this> */
public function assigneeWithoutParens(): MorphTo
{
return $this->morphTo(type: 'nullable_relation_morph_to_type', id: 'nullable_relation_morph_to_id');
}
/** @return MorphTo<(BaseModel&CanBeAssigned), $this> */
public function nonNullableAssignee(): MorphTo
{
return $this->morphTo(type: 'relation_morph_to_type', id: 'relation_morph_to_id');
}
}
@@ -1,24 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
'--write' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
}
@@ -1,48 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property string $relation_morph_to_type
* @property int $relation_morph_to_id
* @property string|null $nullable_relation_morph_to_type
* @property int|null $nullable_relation_morph_to_id
* @property-read (BaseModel&CanBeAssigned)|null $assigneeWithParens
* @property-read (BaseModel&CanBeAssigned)|null $assigneeWithoutParens
* @property-read (BaseModel&CanBeAssigned) $nonNullableAssignee
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection whereNullableRelationMorphToId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection whereNullableRelationMorphToType($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection whereRelationMorphToId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection whereRelationMorphToType($value)
* @mixin \Eloquent
*/
class MorphToIntersection extends Model
{
protected $table = 'morphs';
/** @return MorphTo<(BaseModel&CanBeAssigned), $this> */
public function assigneeWithParens(): MorphTo
{
return $this->morphTo(type: 'nullable_relation_morph_to_type', id: 'nullable_relation_morph_to_id');
}
/** @return MorphTo<BaseModel&CanBeAssigned, $this> */
public function assigneeWithoutParens(): MorphTo
{
return $this->morphTo(type: 'nullable_relation_morph_to_type', id: 'nullable_relation_morph_to_id');
}
/** @return MorphTo<(BaseModel&CanBeAssigned), $this> */
public function nonNullableAssignee(): MorphTo
{
return $this->morphTo(type: 'relation_morph_to_type', id: 'relation_morph_to_id');
}
}
@@ -1,14 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
use Illuminate\Database\Eloquent\Model;
// Tests: final class + attribute with nested array argument (e.g. ObservedBy([...]))
#[ObservedByStub([StubObserver::class])]
final class FinalWithNested extends Model
{
protected $table = 'simples';
}
@@ -1,15 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
use Illuminate\Database\Eloquent\Model;
// Tests: multiple consecutive PHP 8 attributes before the class declaration
#[\AllowDynamicProperties]
#[SecondAttribute]
class MultipleAttributes extends Model
{
protected $table = 'simples';
}
@@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
use Illuminate\Database\Eloquent\Model;
#[\AllowDynamicProperties]
class Simple extends Model
{
}
@@ -1,24 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
'--write' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
}
@@ -1,65 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
use Illuminate\Database\Eloquent\Model;
// Tests: final class + attribute with nested array argument (e.g. ObservedBy([...]))
/**
* @property int $id
* @method static \Illuminate\Database\Eloquent\Builder<static>|FinalWithNested newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|FinalWithNested newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|FinalWithNested query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|FinalWithNested whereId($value)
* @mixin \Eloquent
*/
#[ObservedByStub([StubObserver::class])]
final class FinalWithNested extends Model
{
protected $table = 'simples';
}
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
use Illuminate\Database\Eloquent\Model;
// Tests: multiple consecutive PHP 8 attributes before the class declaration
/**
* @property int $id
* @method static \Illuminate\Database\Eloquent\Builder<static>|MultipleAttributes newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MultipleAttributes newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MultipleAttributes query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MultipleAttributes whereId($value)
* @mixin \Eloquent
*/
#[\AllowDynamicProperties]
#[SecondAttribute]
class MultipleAttributes extends Model
{
protected $table = 'simples';
}
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple whereId($value)
* @mixin \Eloquent
*/
#[\AllowDynamicProperties]
class Simple extends Model
{
}
@@ -1,11 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\QueryMethods\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
}
@@ -1,32 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\QueryMethods;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
protected function getEnvironmentSetUp($app)
{
parent::getEnvironmentSetUp($app);
$app['config']->set('ide-helper.write_query_methods', false);
}
public function test(): void
{
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
'--write' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
$this->assertStringNotContainsString("@method static \Illuminate\Database\Eloquent\Builder<static>|Post query()", $this->mockFilesystemOutput);
}
}
@@ -1,156 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\QueryMethods\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
* @property string|null $string_nullable
* @property string $string_not_nullable
* @property string|null $text_nullable
* @property string $text_not_nullable
* @property string|null $medium_text_nullable
* @property string $medium_text_not_nullable
* @property string|null $long_text_nullable
* @property string $long_text_not_nullable
* @property int|null $integer_nullable
* @property int $integer_not_nullable
* @property int|null $tiny_integer_nullable
* @property int $tiny_integer_not_nullable
* @property int|null $small_integer_nullable
* @property int $small_integer_not_nullable
* @property int|null $medium_integer_nullable
* @property int $medium_integer_not_nullable
* @property int|null $big_integer_nullable
* @property int $big_integer_not_nullable
* @property int|null $unsigned_integer_nullable
* @property int $unsigned_integer_not_nullable
* @property int|null $unsigned_tiny_integer_nullable
* @property int $unsigned_tiny_integer_not_nullable
* @property int|null $unsigned_small_integer_nullable
* @property int $unsigned_small_integer_not_nullable
* @property int|null $unsigned_medium_integer_nullable
* @property int $unsigned_medium_integer_not_nullable
* @property int|null $unsigned_big_integer_nullable
* @property int $unsigned_big_integer_not_nullable
* @property float|null $float_nullable
* @property float $float_not_nullable
* @property float|null $double_nullable
* @property float $double_not_nullable
* @property numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
* @property string $enum_not_nullable
* @property string|null $json_nullable
* @property string $json_not_nullable
* @property string|null $jsonb_nullable
* @property string $jsonb_not_nullable
* @property string|null $date_nullable
* @property string $date_not_nullable
* @property string|null $datetime_nullable
* @property string $datetime_not_nullable
* @property string|null $datetimetz_nullable
* @property string $datetimetz_not_nullable
* @property string|null $time_nullable
* @property string $time_not_nullable
* @property string|null $timetz_nullable
* @property string $timetz_not_nullable
* @property string|null $timestamp_nullable
* @property string $timestamp_not_nullable
* @property string|null $timestamptz_nullable
* @property string $timestamptz_not_nullable
* @property int|null $year_nullable
* @property int $year_not_nullable
* @property string|null $binary_nullable
* @property string $binary_not_nullable
* @property string|null $uuid_nullable
* @property string $uuid_not_nullable
* @property string|null $ipaddress_nullable
* @property string $ipaddress_not_nullable
* @property string|null $macaddress_nullable
* @property string $macaddress_not_nullable
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBigIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBigIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBinaryNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBinaryNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBooleanNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBooleanNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereCharNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereCharNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDateNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDateNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDatetimeNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDatetimeNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDatetimetzNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDatetimetzNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDecimalNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDecimalNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDoubleNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDoubleNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereEnumNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereEnumNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereFloatNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereFloatNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereIpaddressNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereIpaddressNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereJsonNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereJsonNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereJsonbNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereJsonbNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereLongTextNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereLongTextNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMacaddressNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMacaddressNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMediumIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMediumIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMediumTextNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMediumTextNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereSmallIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereSmallIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereStringNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereStringNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTextNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTextNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimeNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimeNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimestampNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimestampNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimestamptzNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimestamptzNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimetzNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimetzNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTinyIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTinyIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedBigIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedBigIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedMediumIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedMediumIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedSmallIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedSmallIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedTinyIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedTinyIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUuidNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUuidNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereYearNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereYearNullable($value)
* @mixin \Eloquent
*/
class Post extends Model
{
}
@@ -17,21 +17,6 @@ class ModelWithRelations extends Model
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id'); return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
} }
public function softDeletableWithTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withTrashed();
}
public function softDeletableOnlyTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->onlyTrashed();
}
public function softDeletableWithoutTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withoutTrashed();
}
public function nonSoftDeletable(): BelongsTo public function nonSoftDeletable(): BelongsTo
{ {
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id'); return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
@@ -16,9 +16,6 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletable * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletable
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletableOnlyTrashed
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletableWithTrashed
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletableWithoutTrashed
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query()
@@ -36,21 +33,6 @@ class ModelWithRelations extends Model
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id'); return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
} }
public function softDeletableWithTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withTrashed();
}
public function softDeletableOnlyTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->onlyTrashed();
}
public function softDeletableWithoutTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withoutTrashed();
}
public function nonSoftDeletable(): BelongsTo public function nonSoftDeletable(): BelongsTo
{ {
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id'); return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
@@ -16,9 +16,6 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletable * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletable
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableOnlyTrashed
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletableWithTrashed
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableWithoutTrashed
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query()
@@ -36,21 +33,6 @@ class ModelWithRelations extends Model
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id'); return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
} }
public function softDeletableWithTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withTrashed();
}
public function softDeletableOnlyTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->onlyTrashed();
}
public function softDeletableWithoutTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withoutTrashed();
}
public function nonSoftDeletable(): BelongsTo public function nonSoftDeletable(): BelongsTo
{ {
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id'); return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
@@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UniqueColumn\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $table = 'users_unique';
}
@@ -1,25 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UniqueColumn;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
'--write' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
}
@@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UniqueColumn\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $email
* @property string $password
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
* @mixin \Eloquent
*/
class User extends Model
{
protected $table = 'users_unique';
}
@@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class UsersUniqueTable extends Migration
{
public function up(): void
{
Schema::create('users_unique', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
}
+3
View File
@@ -101,6 +101,7 @@ DOC;
* @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column
* @param mixed $operator * @param mixed $operator
* @param mixed $value * @param mixed $value
* @param string $boolean
* @return \Illuminate\Database\Eloquent\Builder<static> * @return \Illuminate\Database\Eloquent\Builder<static>
* @static * @static
*/ */
@@ -129,6 +130,8 @@ DOC;
* Add a "where null" clause to the query. * Add a "where null" clause to the query.
* *
* @param string|array|\Illuminate\Contracts\Database\Query\Expression $columns * @param string|array|\Illuminate\Contracts\Database\Query\Expression $columns
* @param string $boolean
* @param bool $not
* @return \Illuminate\Database\Eloquent\Builder<static> * @return \Illuminate\Database\Eloquent\Builder<static>
* @static * @static
*/ */