mirror of
https://github.com/barryvdh/laravel-ide-helper.git
synced 2026-08-18 10:07:12 +00:00
Compare commits
25
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cc26683e8e | ||
|
|
5060909c37 | ||
|
|
3a53071650 | ||
|
|
04c8d5dd2f | ||
|
|
3f742fd665 | ||
|
|
e1b1ef74aa | ||
|
|
7fd3b7aff0 | ||
|
|
1002cb567d | ||
|
|
3e0c12ee0e | ||
|
|
ad7e37676f | ||
|
|
9027494e97 | ||
|
|
c3f795e909 | ||
|
|
2fba281b14 | ||
|
|
39bcb60b7c | ||
|
|
16f685aa9f | ||
|
|
392d5cfc80 | ||
|
|
3d8fccd700 | ||
|
|
e9b62b6ee0 | ||
|
|
37dfcf9e14 | ||
|
|
573c9f3f57 | ||
|
|
fd3cfbe09d | ||
|
|
3bd70eafd0 | ||
|
|
5aca186d92 | ||
|
|
148f956272 | ||
|
|
cff155b0d3 |
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Git checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Validate Composer configuration
|
||||
run: composer validate --strict
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
# echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV
|
||||
|
||||
# Drafts your next Release notes as Pull Requests are merged into "master"
|
||||
- uses: release-drafter/release-drafter@v6
|
||||
- uses: release-drafter/release-drafter@v7
|
||||
# (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
|
||||
# with:
|
||||
# config-name: my-config.yml
|
||||
|
||||
@@ -7,25 +7,30 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
php-laravel-integration-tests:
|
||||
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]
|
||||
php: [8.5, 8.4, 8.3, 8.2]
|
||||
laravel: [12.x, 13.x]
|
||||
exclude:
|
||||
- laravel: 13.x
|
||||
php: 8.2
|
||||
|
||||
name: P${{ matrix.php }} - Laravel${{ matrix.laravel }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: src
|
||||
|
||||
@@ -63,7 +68,6 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
|
||||
@@ -7,13 +7,13 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- "*"
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
jobs:
|
||||
php-tests:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
timeout-minutes: 15
|
||||
|
||||
env:
|
||||
COMPOSER_NO_INTERACTION: 1
|
||||
|
||||
@@ -22,22 +22,26 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
php: [8.5, 8.4, 8.3, 8.2]
|
||||
laravel: [~11.15, 12.x]
|
||||
laravel: [12.x, 13.x, ~11.15]
|
||||
stability: [prefer-lowest, prefer-stable]
|
||||
include:
|
||||
- os: windows-latest
|
||||
php: 8.3
|
||||
laravel: 12.x
|
||||
stability: prefer-stable
|
||||
exclude:
|
||||
- laravel: 13.x
|
||||
php: 8.2
|
||||
|
||||
steps:
|
||||
- name: Set git to use LF
|
||||
if: ${{ matrix.os == 'windows-latest' }}
|
||||
if: "${{ matrix.os == 'windows-latest' }}"
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.event.release.target_commitish }}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.phpunit.result.cache
|
||||
/auth.json
|
||||
/build
|
||||
/.idea
|
||||
/.php-cs-fixer.cache
|
||||
|
||||
@@ -1,5 +1,38 @@
|
||||
# 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
|
||||
|
||||
### What's Changed
|
||||
|
||||
+11
-11
@@ -25,21 +25,21 @@
|
||||
"ext-json": "*",
|
||||
"barryvdh/reflection-docblock": "^2.4",
|
||||
"composer/class-map-generator": "^1.0",
|
||||
"illuminate/console": "^11.15 || ^12",
|
||||
"illuminate/database": "^11.15 || ^12",
|
||||
"illuminate/filesystem": "^11.15 || ^12",
|
||||
"illuminate/support": "^11.15 || ^12"
|
||||
"illuminate/console": "^11.15 || ^12 || ^13.0",
|
||||
"illuminate/database": "^11.15 || ^12 || ^13.0",
|
||||
"illuminate/filesystem": "^11.15 || ^12 || ^13.0",
|
||||
"illuminate/support": "^11.15 || ^12 || ^13.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-pdo_sqlite": "*",
|
||||
"friendsofphp/php-cs-fixer": "^3",
|
||||
"illuminate/config": "^11.15 || ^12",
|
||||
"illuminate/view": "^11.15 || ^12",
|
||||
"illuminate/config": "^11.15 || ^12 || ^13.0",
|
||||
"illuminate/view": "^11.15 || ^12 || ^13.0",
|
||||
"larastan/larastan": "^3.1",
|
||||
"mockery/mockery": "^1.4",
|
||||
"orchestra/testbench": "^9.2 || ^10",
|
||||
"orchestra/testbench": "^9.2 || ^10 || ^11.0",
|
||||
"phpstan/phpstan-phpunit": "^2.0",
|
||||
"phpunit/phpunit": "^10.5 || ^11.5.3",
|
||||
"phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.12",
|
||||
"spatie/phpunit-snapshot-assertions": "^4 || ^5",
|
||||
"vlucas/phpdotenv": "^5"
|
||||
},
|
||||
@@ -66,7 +66,7 @@
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.5-dev"
|
||||
"dev-master": "3.7-dev"
|
||||
},
|
||||
"laravel": {
|
||||
"providers": [
|
||||
@@ -76,6 +76,7 @@
|
||||
},
|
||||
"scripts": {
|
||||
"analyze": "phpstan",
|
||||
"analyze-set-baseline": "phpstan --generate-baseline",
|
||||
"check-style": [
|
||||
"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"
|
||||
@@ -84,9 +85,8 @@
|
||||
"php-cs-fixer fix",
|
||||
"php-cs-fixer fix --config=.php-cs-fixer.tests.php"
|
||||
],
|
||||
"analyze-set-baseline": "phpstan --generate-baseline",
|
||||
"test": "phpunit",
|
||||
"test-ci": "phpunit -d --without-creating-snapshots",
|
||||
"test-ci": "phpunit",
|
||||
"test-regenerate": "phpunit -d --update-snapshots"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,17 @@ return [
|
||||
|
||||
'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
|
||||
|
||||
+18
-12
@@ -101,7 +101,12 @@ class Alias
|
||||
}
|
||||
|
||||
if ($facade === '\Illuminate\Database\Eloquent\Model') {
|
||||
$this->usedMethods = ['decrement', 'increment'];
|
||||
$this->usedMethods = [
|
||||
'decrement' => true,
|
||||
'decrementEach' => true,
|
||||
'increment' => true,
|
||||
'incrementEach' => true,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +192,7 @@ class Alias
|
||||
public function shouldExtendParentClass()
|
||||
{
|
||||
return $this->parentClass
|
||||
&& $this->getExtendsNamespace() !== '\\Illuminate\\Support\\Facades';
|
||||
&& !is_subclass_of($this->extends, Facade::class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +386,7 @@ class Alias
|
||||
$method = new \ReflectionMethod($className, $name);
|
||||
$class = new ReflectionClass($className);
|
||||
|
||||
if (!in_array($magic, $this->usedMethods)) {
|
||||
if (!isset($this->usedMethods[$magic])) {
|
||||
if ($class !== $this->root) {
|
||||
$this->methods[] = new Method(
|
||||
$method,
|
||||
@@ -393,7 +398,7 @@ class Alias
|
||||
$this->getTemplateNames()
|
||||
);
|
||||
}
|
||||
$this->usedMethods[] = $magic;
|
||||
$this->usedMethods[$magic] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -410,7 +415,7 @@ class Alias
|
||||
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
|
||||
if ($methods) {
|
||||
foreach ($methods as $method) {
|
||||
if (!in_array($method->name, $this->usedMethods)) {
|
||||
if (!isset($this->usedMethods[$method->name])) {
|
||||
// Only add the methods to the output when the root is not the same as the class.
|
||||
// And don't add the __*() methods
|
||||
if ($this->extends !== $class && substr($method->name, 0, 2) !== '__') {
|
||||
@@ -424,7 +429,7 @@ class Alias
|
||||
$this->getTemplateNames(),
|
||||
);
|
||||
}
|
||||
$this->usedMethods[] = $method->name;
|
||||
$this->usedMethods[$method->name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -435,7 +440,7 @@ class Alias
|
||||
$properties = $reflection->getStaticProperties();
|
||||
$macros = isset($properties['macros']) ? $properties['macros'] : [];
|
||||
foreach ($macros as $macro_name => $macro_func) {
|
||||
if (!in_array($macro_name, $this->usedMethods)) {
|
||||
if (!isset($this->usedMethods[$macro_name])) {
|
||||
try {
|
||||
$method = $this->getMacroFunction($macro_func);
|
||||
} catch (Throwable $e) {
|
||||
@@ -451,7 +456,7 @@ class Alias
|
||||
$this->classAliases,
|
||||
$this->getReturnTypeNormalizers($reflection)
|
||||
);
|
||||
$this->usedMethods[] = $macro_name;
|
||||
$this->usedMethods[$macro_name] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -589,12 +594,13 @@ class Alias
|
||||
*/
|
||||
protected function removeDuplicateMethodsFromPhpDoc()
|
||||
{
|
||||
$methodNames = array_map(function (Method $method) {
|
||||
return $method->getName();
|
||||
}, $this->getMethods());
|
||||
$methodNames = [];
|
||||
foreach ($this->getMethods() as $method) {
|
||||
$methodNames[$method->getName()] = true;
|
||||
}
|
||||
|
||||
foreach ($this->phpdoc->getTags() as $tag) {
|
||||
if ($tag instanceof MethodTag && in_array($tag->getMethodName(), $methodNames)) {
|
||||
if ($tag instanceof MethodTag && isset($methodNames[$tag->getMethodName()])) {
|
||||
$this->phpdoc->deleteTag($tag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,6 +195,18 @@ class MetaCommand extends Command
|
||||
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.");
|
||||
};
|
||||
|
||||
|
||||
+127
-21
@@ -44,6 +44,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Illuminate\Database\Schema\Builder;
|
||||
use Illuminate\Filesystem\Filesystem;
|
||||
use Illuminate\Support\Arr;
|
||||
@@ -123,6 +124,14 @@ class ModelsCommand extends Command
|
||||
protected $reset;
|
||||
protected $phpstorm_noinspections;
|
||||
protected $write_model_external_builder_methods;
|
||||
/**
|
||||
* @var array<string, \SplFileObject>
|
||||
*/
|
||||
protected $fileCache = [];
|
||||
/**
|
||||
* @var array<string, Context>
|
||||
*/
|
||||
protected $contextCache = [];
|
||||
/**
|
||||
* @var array<string, true>
|
||||
*/
|
||||
@@ -526,7 +535,7 @@ class ModelsCommand extends Command
|
||||
}
|
||||
|
||||
if ($isNullable) {
|
||||
$type .= '|null';
|
||||
$type = $this->wrapIntersectionType($type) . '|null';
|
||||
} else {
|
||||
$type = str_replace($nullString, '', $type);
|
||||
}
|
||||
@@ -534,6 +543,22 @@ class ModelsCommand extends Command
|
||||
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.
|
||||
*
|
||||
@@ -696,13 +721,16 @@ class ModelsCommand extends Command
|
||||
);
|
||||
$this->setMethod($name, $builder . '<static>|' . $modelName, $args, $comment);
|
||||
}
|
||||
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) {
|
||||
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])
|
||||
) {
|
||||
if ($this->laravel['config']->get('ide-helper.write_query_methods', true)) {
|
||||
$builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery()));
|
||||
|
||||
$this->setMethod(
|
||||
$method,
|
||||
$builder . '<static>|' . $this->getClassNameInDestinationFile($model, get_class($model))
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->write_model_external_builder_methods) {
|
||||
$this->writeModelExternalBuilderMethods($model);
|
||||
@@ -721,14 +749,19 @@ class ModelsCommand extends Command
|
||||
$type = (string)$this->getReturnTypeFromDocBlock($reflection);
|
||||
}
|
||||
|
||||
$file = new \SplFileObject($reflection->getFileName());
|
||||
$fileName = $reflection->getFileName();
|
||||
if (!isset($this->fileCache[$fileName])) {
|
||||
$this->fileCache[$fileName] = new \SplFileObject($fileName);
|
||||
}
|
||||
$file = $this->fileCache[$fileName];
|
||||
$file->seek($reflection->getStartLine() - 1);
|
||||
|
||||
$code = '';
|
||||
$lines = [];
|
||||
while ($file->key() < $reflection->getEndLine()) {
|
||||
$code .= $file->current();
|
||||
$lines[] = $file->current();
|
||||
$file->next();
|
||||
}
|
||||
$code = implode('', $lines);
|
||||
$code = trim(preg_replace('/\s\s+/', '', $code));
|
||||
$begin = strpos($code, 'function(');
|
||||
$code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
|
||||
@@ -911,13 +944,55 @@ class ModelsCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->relatedModelUsesSoftDeletes($relationObj)) {
|
||||
if (
|
||||
$this->relatedModelUsesSoftDeletes($relationObj)
|
||||
&& !$this->relationIncludesNonTrashedParents($relationObj)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
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
|
||||
*
|
||||
@@ -982,7 +1057,7 @@ class ModelsCommand extends Command
|
||||
if ($type !== null) {
|
||||
$newType = $this->getTypeOverride($type);
|
||||
if ($nullable) {
|
||||
$newType .= '|null';
|
||||
$newType = $this->wrapIntersectionType($newType) . '|null';
|
||||
}
|
||||
$this->properties[$name]['type'] = $newType;
|
||||
}
|
||||
@@ -1170,6 +1245,14 @@ class ModelsCommand extends Command
|
||||
$replace = "{$modelDocComment}\n";
|
||||
$pos = strpos($contents, "final class {$classname}") ?: strpos($contents, "class {$classname}");
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1275,13 +1358,18 @@ class ModelsCommand extends Command
|
||||
}
|
||||
}
|
||||
|
||||
protected ?array $cachedRelationTypes = null;
|
||||
protected ?array $cachedRelationReturnTypes = null;
|
||||
|
||||
/**
|
||||
* Returns the available relation types
|
||||
*/
|
||||
protected function getRelationTypes(): array
|
||||
{
|
||||
$configuredRelations = $this->laravel['config']->get('ide-helper.additional_relation_types', []);
|
||||
return array_merge(self::RELATION_TYPES, $configuredRelations);
|
||||
return $this->cachedRelationTypes ??= array_merge(
|
||||
self::RELATION_TYPES,
|
||||
$this->laravel['config']->get('ide-helper.additional_relation_types', [])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1289,7 +1377,7 @@ class ModelsCommand extends Command
|
||||
*/
|
||||
protected function getRelationReturnTypes(): array
|
||||
{
|
||||
return $this->laravel['config']->get('ide-helper.additional_relation_return_types', []);
|
||||
return $this->cachedRelationReturnTypes ??= $this->laravel['config']->get('ide-helper.additional_relation_return_types', []);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1364,11 +1452,7 @@ class ModelsCommand extends Command
|
||||
*/
|
||||
protected function getCommentFromDocBlock(\ReflectionMethod $reflection)
|
||||
{
|
||||
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
|
||||
$context = new Context(
|
||||
$phpDocContext->getNamespace(),
|
||||
$phpDocContext->getNamespaceAliases()
|
||||
);
|
||||
$context = $this->getDocBlockContext($reflection);
|
||||
$comment = '';
|
||||
$phpdoc = new DocBlock($reflection, $context);
|
||||
|
||||
@@ -1389,11 +1473,7 @@ class ModelsCommand extends Command
|
||||
*/
|
||||
protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection, ?\Reflector $reflectorForContext = null)
|
||||
{
|
||||
$phpDocContext = (new ContextFactory())->createFromReflector($reflectorForContext ?? $reflection);
|
||||
$context = new Context(
|
||||
$phpDocContext->getNamespace(),
|
||||
$phpDocContext->getNamespaceAliases()
|
||||
);
|
||||
$context = $this->getDocBlockContext($reflectorForContext ?? $reflection);
|
||||
$type = null;
|
||||
$phpdoc = new DocBlock($reflection, $context);
|
||||
|
||||
@@ -1411,6 +1491,31 @@ class ModelsCommand extends Command
|
||||
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
|
||||
{
|
||||
$returnType = $reflection->getReturnType();
|
||||
@@ -1616,9 +1721,10 @@ class ModelsCommand extends Command
|
||||
*/
|
||||
protected function getUsedClassNames(ReflectionClass $reflection): array
|
||||
{
|
||||
$context = $this->getDocBlockContext($reflection);
|
||||
$namespaceAliases = array_flip(array_map(function ($alias) {
|
||||
return ltrim($alias, '\\');
|
||||
}, (new ContextFactory())->createFromReflector($reflection)->getNamespaceAliases()));
|
||||
}, $context->getNamespaceAliases()));
|
||||
$namespaceAliases[$reflection->getName()] = $reflection->getShortName();
|
||||
|
||||
return $namespaceAliases;
|
||||
|
||||
@@ -76,6 +76,8 @@ class Method
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
|
||||
$this->removePhpDocParamTagsForOptionalParameters();
|
||||
|
||||
//Get the parameters, including formatted default values
|
||||
$this->getParameters($method);
|
||||
|
||||
@@ -248,6 +250,50 @@ 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
|
||||
*
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
abstract class BaseModel extends Model
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
|
||||
|
||||
interface CanBeAssigned
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?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';
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<?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';
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
#[\AllowDynamicProperties]
|
||||
class Simple extends Model
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?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
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\QueryMethods\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Post extends Model
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?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,6 +17,21 @@ class ModelWithRelations extends Model
|
||||
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
|
||||
{
|
||||
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
|
||||
|
||||
+18
@@ -16,6 +16,9 @@ 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\SoftDeletableModel $softDeletable
|
||||
* @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 newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query()
|
||||
@@ -33,6 +36,21 @@ class ModelWithRelations extends Model
|
||||
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
|
||||
{
|
||||
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
|
||||
|
||||
@@ -16,6 +16,9 @@ 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\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 $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 newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query()
|
||||
@@ -33,6 +36,21 @@ class ModelWithRelations extends Model
|
||||
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
|
||||
{
|
||||
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?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';
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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';
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?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();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -101,7 +101,6 @@ DOC;
|
||||
* @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column
|
||||
* @param mixed $operator
|
||||
* @param mixed $value
|
||||
* @param string $boolean
|
||||
* @return \Illuminate\Database\Eloquent\Builder<static>
|
||||
* @static
|
||||
*/
|
||||
@@ -130,8 +129,6 @@ DOC;
|
||||
* Add a "where null" clause to the query.
|
||||
*
|
||||
* @param string|array|\Illuminate\Contracts\Database\Query\Expression $columns
|
||||
* @param string $boolean
|
||||
* @param bool $not
|
||||
* @return \Illuminate\Database\Eloquent\Builder<static>
|
||||
* @static
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user