Compare commits

..
46 Commits
Author SHA1 Message Date
Markus Podar 999167d4c2 Prepare 2.12.1 patch release (#1307)
### Fixed
- Properly handle `Castable`s without return type. [#1306 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1306)
2022-01-24 22:36:43 +01:00
BinotaLIU 2673bf4889 return mixed if we failed detect castable type (#1306)
fixed #1305
2022-01-24 21:39:17 +01:00
Markus Podar 771770e56c Merge pull request #1304 from mfn/mfn-release
Prepare 2.12 release
2022-01-23 17:50:53 +01:00
Barry vd. Heuvel 88353535f2 Update composer.json 2022-01-23 17:46:42 +01:00
mfn acb2df9dfb normalize composer.json 2022-01-23 16:39:03 +00:00
Markus Podar 7052b4f708 Prepare 2.12 release
### Added
- Add support for custom casts that using `Castable` [#1287 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1287)
- Added Laravel 9 support [#1297 / rcerljenko](https://github.com/barryvdh/laravel-ide-helper/pull/1297)
2022-01-23 17:38:31 +01:00
Yehuda Katz ac1e27167d Disabled PHPCS (#1301)
PHPCS is a common development tool. It makes sense to disable it in addition to Eclipse/IntelliJ formatting disable
2022-01-20 14:27:58 +01:00
de64d665e0 added laravel 9 support (#1297)
* added laravel 9 support

* added exclude directives to matrix

* fixed matrix

* Update composer.json

* Update run-integration-tests.yml

* Update CHANGELOG.md

Co-authored-by: Ricardo Čerljenko <[email protected]>
Co-authored-by: Barry vd. Heuvel <[email protected]>
2022-01-13 15:38:59 +01:00
Markus Podar 93d987336b Adapt/fix changelog now that a new release was made (#1294)
Triggered by https://github.com/barryvdh/laravel-ide-helper/pull/1287#issuecomment-1008344636
2022-01-09 22:26:59 +01:00
BinotaLIU 744a9cf380 support Castables (#1287) 2022-01-09 18:52:15 +01:00
Markus Podar f2a7f9d84f Improve performance for supporting Laravel 8.77+ cast attributes (#1292)
#1289 calls `getReturnType` for every method on every model, which in
turn calls `getReturnTypeFromDocBlock` which has this code:
```php
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
```
Extracting the docblock is super slow, always has been. Now that we do
this for every method, this adds up a lot.

Performance on a private commercial project _before_ #1289 was introduced:
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.857s
user	0m1.835s
sys	0m0.129s
```
After #1289 :
```
$ time ./artisan ide-helper:models --write --reset >/dev/null

real	0m54.147s
user	0m47.132s
sys	0m1.047s
```

However, in this case we **do not need** the phpdoc fallback (which is
legitimate and by design for many other cases), because also the Laravel
implementation only works by inspecting the _actual type_, see
https://github.com/laravel/framework/blob/e0c2620b57be6416820ea7ca8e46fd2f71d2fe35/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php#L570-L575
```php
$returnType = (new ReflectionMethod($this, $method))->getReturnType();

return static::$attributeMutatorCache[get_class($this)][$key] = $returnType &&
            $returnType instanceof ReflectionNamedType &&
            $returnType->getName() === Attribute::class &&
            is_callable($this->{$method}()->get);
```

This side-stepping the phpdoc parsing a) still works correctly and b)
brings us back to the previous performance characteristics:
```
time ./artisan ide-helper:models --write --reset >/dev/null

real	0m2.987s
user	0m1.915s
sys	0m0.120s
```
2022-01-03 21:47:54 +01:00
Simon Jønsson 29dd57e1e2 Support Laravel 8.77 Attributes (#1289)
*  Set properties from model functions returning an Attribute

*  Add test for model Attributes

* 🎨 Fix code style

* 🔖 Update changelog

*  Update test to not require php 8

* 🐛 Fix PHP 7.3 incompatibility

*  Update tests to only run when Illuminate Attribute exists
2022-01-03 10:02:18 +01:00
Maduka Jayalath 6244a931a0 Update README.md (#1290)
Shorten links to texts from artisan commands so it will be easy to copy those commands to use.
2022-01-03 07:40:25 +01:00
Michael Newton ebea96213d Improve casts support (#1262)
* support for all possible `Model::$casts` types

* add testing for casts

* update changelog

* update changelog (again?)
2021-11-14 17:40:15 +01:00
Sergiy Petrov 01be015f86 PHP 8.1 Support (#1274)
* test against php 8.1

* fix tests
2021-10-31 14:55:33 +01:00
Silvan Krähenbühl a091b9c94a Allow casts without property (#1267)
* Allow casts without property

* Update changelog
2021-10-25 13:45:30 +02:00
George Boot dd12b7cdcb Generate cleaner model mixin classes (#1268)
* Generate cleaner model mixin classes

* Fix test
2021-10-25 13:44:43 +02:00
Stephan Tijink ae48e1e467 Update dependency composer/composer to 1.10.23 and 2.1.9 due to security issue (#1271)
* Update composer/composer to 2.0.23

* Update composer/composer to 2.1.9

* Also update composer/composer 1.x
2021-10-25 09:09:49 +02:00
Wim Reckmanandlaravel-ide-helper c733bde1ea Move default models filename to config (#1241)
* Move default models filename to config

* Add default value when getting models helper filename from config

* composer fix-style

Co-authored-by: laravel-ide-helper <[email protected]>
2021-08-10 07:12:56 +02:00
atsu.kg 52a4b64656 Consider foreign key constraints with isRelationNullable (#1231)
* Consider foreign key constraints with isRelationNullable

* test: add belongsTo Variation

* command: composer test-regenerate
2021-08-07 13:31:20 +02:00
Barry vd. Heuvel b268871c16 Fix tests 2021-08-04 11:40:36 +02:00
Ajay Dwivedi 2fc239623b Fix #1173 Use FQN for write-mixin (#1174)
* Fix #1173 Use FQN for write-mixin

* Fix #1173 Use FQN for write-mixin: Update Test cases
2021-08-04 11:26:51 +02:00
37f08320ea Make tests pass on Windows (#1205)
* Make all tests pass cross platform

* Add job to run tests on Windows

* Merge test jobs with conditional Windows step for line endings

* Prefix job name with OS

* Update git lf step if statement

* Exclude all tests for Windows except PHP 8.0

* composer fix-style

Co-authored-by: Barry vd. Heuvel <[email protected]>
Co-authored-by: laravel-ide-helper <[email protected]>
2021-08-04 11:22:48 +02:00
ZeroThe2nd b6871d09a4 Bugfix/custom types cause exception (#1228)
* bugfix/custom-types-cause-exception

- Added use statements for DBAL Exception and Type
- Added @throws tag to getPropertiesFromTable method
- Added Type::addType() to properly register types
- Added Try-Catch around Type::addType() to give users a proper error message, then throw the exception up.

* bugfix/custom-types-cause-exception

- Updated CHANGELOG.MD

* bugfix/custom-types-cause-exception

- Fixed `Type enum_string_boolean already exists.` error from being triggered when multiple models are loaded
2021-08-04 11:18:55 +02:00
Karel Faille 3b75048d12 Support variadic parameters in models command (#1234) 2021-08-04 11:13:23 +02:00
Jan Misker b162347aff fix when no enclosingClass can be found (#1239)
In certain situations no enclosingClass can be found, quick fix is to just return then.
2021-08-04 11:12:34 +02:00
Richard van Baarsen f4e0fc3695 Allow for PhpDoc for macros with union types (#1148)
* Allow for PhpDoc for macros with union types

* Replace null coalescing operator

* Test init PhpDoc for macros with parameter union types

* Allow for PhpDoc for macros with union return types

* Add helper method to Macro class

* Add changelog entry

* Format

* Complement changelog with PR link

* Add missing return

* Fix test for PHP < 8

* Rewrite PHP 8 test using eval()

* Remove obsolete test class

* Suppress Psalm errors for undefined ReflectionUnionType class

* Remove unreachable return statement
2021-07-30 08:54:59 +02:00
Alexander StockandMarkus Podar af84c78dec Use platformName to determine db type (#1212)
* Use current connection platform (driver) instead of default connection name to determine used dbms. Previous solution isn't working, when multiple connections with custom names are used.

* chore: add changelog

Co-authored-by: Markus Podar <[email protected]>
2021-06-20 10:38:04 +02:00
Daniël de WitandMarkus Podar e71f3d5fc0 Check for traits recursively (#1216)
* Check for traits recursively

* Remove autoload bool from class_uses_recursive

* Add test

* Update src/Console/ModelsCommand.php

Co-authored-by: Markus Podar <[email protected]>

* Updated changelog

Co-authored-by: Markus Podar <[email protected]>
2021-06-20 10:18:14 +02:00
Markus Podar df670747c3 composer.json: remove --prefer-lowest (#1217)
IMHO it's impractical to expect this package to have to work with the
"first release of Laravel for any major release" even when there are
already sever more releases: no one is/should stick to that version.

This is a re-submit of https://github.com/barryvdh/laravel-ide-helper/pull/1076

See also:
- https://github.com/barryvdh/laravel-ide-helper/pull/1216#issuecomment-831029022
- https://github.com/barryvdh/laravel-ide-helper/pull/1185#issuecomment-831029353
2021-06-20 10:11:57 +02:00
Barry vd. Heuvel 3a4eee9487 Update composer.json 2021-06-12 10:09:56 +02:00
Barry vd. Heuvel eb8c5729ae Update FUNDING.yml 2021-06-12 10:09:29 +02:00
Markus Podar dc5282e9a4 Merge pull request #1211 from madalinignisca/patch-1
Fix link to DBAL types
2021-04-13 20:37:23 +02:00
Madalin IgniscaandMarkus Podar c82529babd Update config/ide-helper.php
Let's hope users know to switch to the version locked by composer.

Co-authored-by: Markus Podar <[email protected]>
2021-04-13 11:46:59 +02:00
Madalin Ignisca 7cf8a51a8b Fix link to DBAL types
Documentation has been moved, old reference landing on 404.
Pointed to 2.x as for the moment we end up with 2.x because of many other package dependencies.
2021-04-12 06:50:05 +02:00
Markus Podarandlaravel-ide-helper 73b1012b92 chore: prepare 2.10.0 release (#1207)
* chore: prepare 2.10.0 release

* composer fix-style

Co-authored-by: laravel-ide-helper <[email protected]>
2021-04-09 08:17:55 +02:00
Terence MarksandTerence Marks ca94ffd99a Fixing issue where incorrect autoloader unregistered (#1210)
Co-authored-by: Terence Marks <[email protected]>
2021-04-09 07:54:00 +02:00
Markus Podar c1ddd30532 Merge pull request #1198 from jenga201/master
Allowing Methods to be set or unset in ModelHooks
2021-04-03 20:55:37 +02:00
Markus Podar dcbe6c953f chore: update changelog 2021-04-03 00:17:10 +02:00
Markus Podar 867a6fd9b4 Merge branch 'master' into jenga201_master 2021-04-03 00:15:42 +02:00
Rob Porter 39a96c3b95 run composer fix-style 2021-04-02 15:30:15 -04:00
Rob Porter 057efb0ee7 Adding tests to Set / unset methods 2021-04-02 15:25:22 -04:00
Markus Podar 1010cdbd26 chore: prepare changelog for 2.9.3 release (#1203) 2021-04-02 19:55:52 +02:00
wimski 2f61602e7a Support both customized namespace factories as well as default resolvable ones (#1201) 2021-04-02 16:32:13 +02:00
Rob Porter 9fe65e7ed3 Updating readme with a ModelHooks set/unset Method example 2021-04-01 16:04:59 -04:00
Rob Porter 16a4297992 Allowing Methods to be set in ModelHooks 2021-04-01 15:55:14 -04:00
48 changed files with 1260 additions and 150 deletions
+2 -9
View File
@@ -1,12 +1,5 @@
# These are supported funding model platforms
github: barryvdh
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
custom: ['https://fruitcake.nl']
+1 -1
View File
@@ -18,7 +18,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
php-version: 8.0
coverage: none
- name: Install dependencies
+7 -2
View File
@@ -18,7 +18,7 @@ jobs:
COMPOSER_NO_INTERACTION: 1
strategy:
matrix:
php: [8.0, 7.4, 7.3]
php: [8.1, 8.0, 7.4, 7.3]
lumen: [8.*]
name: P${{ matrix.php }} - Lumen${{ matrix.lumen }}
steps:
@@ -75,8 +75,13 @@ jobs:
COMPOSER_NO_INTERACTION: 1
strategy:
matrix:
php: [8.0, 7.4, 7.3]
php: [8.1, 8.0, 7.4, 7.3]
laravel: [8.*]
exclude:
- php: 7.4
laravel: 9.*
- php: 7.3
laravel: 9.*
name: P${{ matrix.php }} - Laravel${{ matrix.laravel }}
steps:
- name: Checkout code
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
php-version: 8.0
coverage: none
extensions: pdo_sqlite
+20 -8
View File
@@ -12,23 +12,35 @@ on:
jobs:
php-tests:
runs-on: ubuntu-20.04
runs-on: ${{ matrix.os }}
timeout-minutes: 15
env:
COMPOSER_NO_INTERACTION: 1
strategy:
matrix:
php: [8.0, 7.4, 7.3]
laravel: [8.*]
os: [ubuntu-20.04, windows-2019]
php: [8.1, 8.0, 7.4, 7.3]
laravel: [8.*, 9.*]
dependency-version: [prefer-lowest, prefer-stable]
exclude:
- php: 8.0
dependency-version: prefer-lowest
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }}
- php: 7.4
laravel: 9.*
- php: 7.3
laravel: 9.*
- os: windows-2019
php: 7.4
- os: windows-2019
php: 7.3
steps:
- name: Set git to use LF
if: ${{ matrix.os == 'windows-2019' }}
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout code
uses: actions/checkout@v2
@@ -37,14 +49,14 @@ jobs:
with:
php-version: ${{ matrix.php }}
coverage: none
extensions: pdo_sqlite
extensions: pdo_sqlite, fileinfo
- name: Install dependencies
run: |
composer remove vimeo/psalm --no-update --dev
composer remove friendsofphp/php-cs-fixer --no-update --dev
composer require "laravel/framework:${{ matrix.laravel }}" --no-update --no-progress
composer update --${{ matrix.dependency-version }} --prefer-dist --no-progress
composer update --prefer-dist --no-progress
- name: Execute Unit Tests
run: composer test-ci
+50 -1
View File
@@ -2,9 +2,57 @@
All notable changes to this project will be documented in this file.
[Next release](https://github.com/barryvdh/laravel-ide-helper/compare/v2.9.3...master)
[Next release](https://github.com/barryvdh/laravel-ide-helper/compare/v2.12.1...master)
--------------
2022-01-24, 2.12.1
------------------
### Fixed
- Properly handle `Castable`s without return type. [#1306 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1306)
2022-01-23, 2.12.0
------------------
### Added
- Add support for custom casts that using `Castable` [#1287 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1287)
- Added Laravel 9 support [#1297 / rcerljenko](https://github.com/barryvdh/laravel-ide-helper/pull/1297)
2022-01-03, 2.11.0
------------------
### Added
- Add support for Laravel 8.77 Attributes [\#1289 / SimonJnsson](https://github.com/barryvdh/laravel-ide-helper/pull/1289)
- Add support for cast types `decimal:*`, `encrypted:*`, `immutable_date`, `immutable_datetime`, `custom_datetime`, and `immutable_custom_datetime` [#1262 / miken32](https://github.com/barryvdh/laravel-ide-helper/pull/1262)
- Add support of variadic parameters in `ide-helper:models` [\#1234 / shaffe-fr](https://github.com/barryvdh/laravel-ide-helper/pull/1234)
- Add support of custom casts without properties [\#1267 / sparclex](https://github.com/barryvdh/laravel-ide-helper/pull/1267)
### Fixed
- Fix recursively searching for `HasFactory` and `Macroable` traits [\#1216 / daniel-de-wit](https://github.com/barryvdh/laravel-ide-helper/pull/1216)
- Use platformName to determine db type when casting boolean types [\#1212 / stockalexander](https://github.com/barryvdh/laravel-ide-helper/pull/1212)
### Changed
- Move default models helper filename to config [\#1241 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
2021-06-18, 2.10.1
------------------
### Added
- Added Type registration according to [Custom Mapping Types documentation](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#custom-mapping-types) [\#1228 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
### Fixed
- Fixing issue where configured custom_db_types could cause a DBAL exception to be thrown while running `ide-helper:models` [\#1228 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
2021-04-09, 2.10.0
------------------
### Added
- Allowing Methods to be set or unset in ModelHooks [\#1198 / jenga201](https://github.com/barryvdh/laravel-ide-helper/pull/1198)\
Note: the visibility of `\Barryvdh\LaravelIdeHelper\Console\ModelsCommand::setMethod` has been changed to **public**!
### Fixed
- Fixing issue where incorrect autoloader unregistered [\#1210 / tezhm](https://github.com/barryvdh/laravel-ide-helper/pull/1210)
2021-04-02, 2.9.3
-----------------
### Fixed
- Support both customized namespace factories as well as default resolvable ones [\#1201 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1201)
2021-04-01, 2.9.2
-----------------
### Added
@@ -21,6 +69,7 @@ All notable changes to this project will be documented in this file.
- Generate PHPDoc for Laravel 8.x factories [\#1074 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1074)
- Add a comment to a property like table columns [\#1168 / biiiiiigmonster](https://github.com/barryvdh/laravel-ide-helper/pull/1168)
- Added `post_migrate` hook to run commands after a migration [\#1163 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1163)
- Allow for PhpDoc for macros with union types [\#1148 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1148)
### Fixed
- Error when generating helper for invokable classes [\#1124 / standaniels](https://github.com/barryvdh/laravel-ide-helper/pull/1124)
+6 -3
View File
@@ -68,9 +68,9 @@ If for some reason you want manually control this:
_Check out [this Laracasts video](https://laracasts.com/series/how-to-be-awesome-in-phpstorm/episodes/15) for a quick introduction/explanation!_
- [`php artisan ide-helper:generate` - PHPDoc generation for Laravel Facades ](#automatic-phpdoc-generation-for-laravel-facades)
- [`php artisan ide-helper:models` - PHPDocs for models](#automatic-PHPDocs-for-models)
- [`php artisan ide-helper:meta` - PhpStorm Meta file](#phpstorm-meta-for-container-instances)
- `php artisan ide-helper:generate` - [PHPDoc generation for Laravel Facades ](#automatic-phpdoc-generation-for-laravel-facades)
- `php artisan ide-helper:models` - [PHPDocs for models](#automatic-PHPDocs-for-models)
- `php artisan ide-helper:meta` - [PhpStorm Meta file](#phpstorm-meta-for-container-instances)
Note: You do need CodeComplice for Sublime Text: https://github.com/spectacles/CodeComplice
@@ -166,6 +166,7 @@ php artisan ide-helper:models "App\Models\Post"
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post query()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post whereTitle($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Models\Post forAuthors(\User ...$authors)
* …
*/
```
@@ -301,6 +302,8 @@ class MyCustomHook implements ModelHookInterface
}
$command->setProperty('custom', 'string', true, false, 'My custom property');
$command->unsetMethod('method');
$command->setMethod('method', $command->getMethodType($model, '\Some\Class'), ['$param']);
}
}
```
+24 -24
View File
@@ -1,6 +1,7 @@
{
"name": "barryvdh/laravel-ide-helper",
"description": "Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.",
"license": "MIT",
"keywords": [
"laravel",
"autocomplete",
@@ -12,7 +13,6 @@
"codeintel",
"phpdoc"
],
"license": "MIT",
"authors": [
{
"name": "Barry vd. Heuvel",
@@ -23,41 +23,30 @@
"php": "^7.3 || ^8.0",
"ext-json": "*",
"barryvdh/reflection-docblock": "^2.0.6",
"composer/composer": "^1.6 || ^2",
"composer/composer": "^1.10.23 || ^2.1.9",
"doctrine/dbal": "^2.6 || ^3",
"illuminate/console": "^8",
"illuminate/filesystem": "^8",
"illuminate/support": "^8",
"illuminate/console": "^8 || ^9",
"illuminate/filesystem": "^8 || ^9",
"illuminate/support": "^8 || ^9",
"nikic/php-parser": "^4.7",
"phpdocumentor/type-resolver": "^1.1.0"
},
"require-dev": {
"ext-pdo_sqlite": "*",
"friendsofphp/php-cs-fixer": "^2",
"illuminate/config": "^8",
"illuminate/view": "^8",
"illuminate/config": "^8 || ^9",
"illuminate/view": "^8 || ^9",
"mockery/mockery": "^1.4",
"orchestra/testbench": "^6",
"orchestra/testbench": "^6 || ^7",
"phpunit/phpunit": "^8.5 || ^9",
"spatie/phpunit-snapshot-assertions": "^3 || ^4",
"vimeo/psalm": "^3.12"
},
"suggest": {
"illuminate/events": "Required for automatic helper generation (^6|^7|^8)."
},
"config": {
"sort-packages": true
},
"extra": {
"branch-alias": {
"dev-master": "2.9-dev"
},
"laravel": {
"providers": [
"Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider"
]
}
"illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9)."
},
"minimum-stability": "dev",
"prefer-stable": true,
"autoload": {
"psr-4": {
"Barryvdh\\LaravelIdeHelper\\": "src"
@@ -68,8 +57,19 @@
"Barryvdh\\LaravelIdeHelper\\Tests\\": "tests"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"sort-packages": true
},
"extra": {
"branch-alias": {
"dev-master": "2.12-dev"
},
"laravel": {
"providers": [
"Barryvdh\\LaravelIdeHelper\\IdeHelperServiceProvider"
]
}
},
"scripts": {
"analyze": "psalm",
"check-style": [
+12 -1
View File
@@ -13,6 +13,17 @@ return [
'filename' => '_ide_helper.php',
/*
|--------------------------------------------------------------------------
| Models filename
|--------------------------------------------------------------------------
|
| The default filename for the models helper file
|
*/
'models_filename' => '_ide_helper_models.php',
/*
|--------------------------------------------------------------------------
| Where to write the PhpStorm specific meta file
@@ -206,7 +217,7 @@ return [
| The value of the array is an array of type mappings. Key is the name of the custom type,
| (for example, "jsonb" from Postgres 9.4) and the value is the name of the corresponding Doctrine2 type (in
| our case it is 'json_array'. Doctrine types are listed here:
| http://doctrine-dbal.readthedocs.org/en/latest/reference/types.html
| https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#types
|
| So to support jsonb in your models when working with Postgres, just add the following entry to the array below:
|
+1
View File
@@ -9,6 +9,7 @@
?>
// @formatter:off
// phpcs:ignoreFile
/**
* A helper file for Laravel, to provide autocomplete information to your IDE
+12 -8
View File
@@ -84,7 +84,7 @@ class MetaCommand extends Command
// Needs to run before exception handler is registered
$factories = $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : [];
$this->registerClassAutoloadExceptions();
$ourAutoloader = $this->registerClassAutoloadExceptions();
$bindings = [];
foreach ($this->getAbstracts() as $abstract) {
@@ -106,7 +106,7 @@ class MetaCommand extends Command
}
}
$this->unregisterClassAutoloadExceptions();
$this->unregisterClassAutoloadExceptions($ourAutoloader);
$content = $this->view->make('meta', [
'bindings' => $bindings,
@@ -143,12 +143,16 @@ class MetaCommand extends Command
/**
* Register an autoloader the throws exceptions when a class is not found.
*
* @return callable
*/
protected function registerClassAutoloadExceptions()
protected function registerClassAutoloadExceptions(): callable
{
spl_autoload_register(function ($class) {
$autoloader = function ($class) {
throw new \ReflectionException("Class '$class' not found.");
});
};
spl_autoload_register($autoloader);
return $autoloader;
}
/**
@@ -167,11 +171,11 @@ class MetaCommand extends Command
/**
* Remove our custom autoloader that we pushed onto the autoload stack
*
* @param callable $ourAutoloader
*/
private function unregisterClassAutoloadExceptions()
private function unregisterClassAutoloadExceptions(callable $ourAutoloader): void
{
$autoloadFunctions = spl_autoload_functions();
$ourAutoloader = array_pop($autoloadFunctions);
spl_autoload_unregister($ourAutoloader);
}
}
+188 -26
View File
@@ -17,8 +17,12 @@ use Barryvdh\Reflection\DocBlock\Context;
use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
use Barryvdh\Reflection\DocBlock\Tag;
use Composer\Autoload\ClassMapGenerator;
use Doctrine\DBAL\Exception as DBALException;
use Doctrine\DBAL\Types\Type;
use Illuminate\Console\Command;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -33,6 +37,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use phpDocumentor\Reflection\Types\ContextFactory;
use ReflectionClass;
@@ -76,7 +81,11 @@ class ModelsCommand extends Command
* @var string
*/
protected $name = 'ide-helper:models';
protected $filename = '_ide_helper_models.php';
/**
* @var string
*/
protected $filename;
/**
* The console command description.
@@ -100,6 +109,10 @@ class ModelsCommand extends Command
* @var bool[string]
*/
protected $nullableColumns = [];
/**
* @var string[]
*/
protected $foreignKeyConstraintsColumns = [];
/**
* During initialization we use Laravels Date Facade to
@@ -125,7 +138,8 @@ class ModelsCommand extends Command
*/
public function handle()
{
$filename = $this->option('filename');
$this->filename = $this->laravel['config']->get('ide-helper.models_filename', '_ide_helper_models.php');
$filename = $this->option('filename') ?? $this->filename;
$this->write = $this->option('write');
$this->write_mixin = $this->option('write-mixin');
$this->dirs = array_merge(
@@ -193,7 +207,7 @@ class ModelsCommand extends Command
protected function getOptions()
{
return [
['filename', 'F', InputOption::VALUE_OPTIONAL, 'The path to the helper file', $this->filename],
['filename', 'F', InputOption::VALUE_OPTIONAL, 'The path to the helper file'],
['dir', 'D', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'The model dir, supports glob patterns', [], ],
['write', 'W', InputOption::VALUE_NONE, 'Write to Model file'],
@@ -344,11 +358,27 @@ class ModelsCommand extends Command
{
$casts = $model->getCasts();
foreach ($casts as $name => $type) {
if (Str::startsWith($type, 'decimal:')) {
$type = 'decimal';
} elseif (Str::startsWith($type, 'custom_datetime:')) {
$type = 'date';
} elseif (Str::startsWith($type, 'immutable_custom_datetime:')) {
$type = 'immutable_date';
} elseif (Str::startsWith($type, 'encrypted:')) {
$type = Str::after($type, ':');
}
$params = [];
switch ($type) {
case 'encrypted':
$realType = 'mixed';
break;
case 'boolean':
case 'bool':
$realType = 'boolean';
break;
case 'decimal':
case 'string':
$realType = 'string';
break;
@@ -373,6 +403,10 @@ class ModelsCommand extends Command
case 'datetime':
$realType = $this->dateClass;
break;
case 'immutable_date':
case 'immutable_datetime':
$realType = '\Carbon\CarbonImmutable';
break;
case 'collection':
$realType = '\Illuminate\Support\Collection';
break;
@@ -381,6 +415,10 @@ class ModelsCommand extends Command
// the `$type` until the `:`
$type = strtok($type, ':');
$realType = class_exists($type) ? ('\\' . $type) : 'mixed';
$this->setProperty($name, null, true, true);
$params = strtok(':');
$params = $params ? explode(',', $params) : [];
break;
}
@@ -388,6 +426,7 @@ class ModelsCommand extends Command
continue;
}
$realType = $this->checkForCastableCasts($realType, $params);
$realType = $this->checkForCustomLaravelCasts($realType);
$realType = $this->getTypeOverride($realType);
$this->properties[$name]['type'] = $this->getTypeInModel($model, $realType);
@@ -415,6 +454,8 @@ class ModelsCommand extends Command
* Load the properties from the database table.
*
* @param \Illuminate\Database\Eloquent\Model $model
*
* @throws DBALException If custom field failed to register
*/
public function getPropertiesFromTable($model)
{
@@ -426,6 +467,14 @@ class ModelsCommand extends Command
$platformName = $databasePlatform->getName();
$customTypes = $this->laravel['config']->get("ide-helper.custom_db_types.{$platformName}", []);
foreach ($customTypes as $yourTypeName => $doctrineTypeName) {
try {
if (!Type::hasType($yourTypeName)) {
Type::addType($yourTypeName, get_class(Type::getType($doctrineTypeName)));
}
} catch (DBALException $exception) {
$this->error("Failed registering custom db type \"$yourTypeName\" as \"$doctrineTypeName\"");
throw $exception;
}
$databasePlatform->registerDoctrineTypeMapping($yourTypeName, $doctrineTypeName);
}
@@ -440,6 +489,7 @@ class ModelsCommand extends Command
return;
}
$this->setForeignKeys($schema, $table);
foreach ($columns as $column) {
$name = $column->getName();
if (in_array($name, $model->getDates())) {
@@ -463,7 +513,7 @@ class ModelsCommand extends Command
$type = 'integer';
break;
case 'boolean':
switch (config('database.default')) {
switch ($platformName) {
case 'sqlite':
case 'mysql':
$type = 'integer';
@@ -519,6 +569,9 @@ class ModelsCommand extends Command
if ($methods) {
sort($methods);
foreach ($methods as $method) {
$reflection = new \ReflectionMethod($model, $method);
$type = $this->getReturnTypeFromReflection($reflection);
$isAttribute = is_a($type, '\Illuminate\Database\Eloquent\Casts\Attribute', true);
if (
Str::startsWith($method, 'get') && Str::endsWith(
$method,
@@ -528,12 +581,25 @@ class ModelsCommand extends Command
//Magic get<name>Attribute
$name = Str::snake(substr($method, 3, -9));
if (!empty($name)) {
$reflection = new \ReflectionMethod($model, $method);
$type = $this->getReturnType($reflection);
$type = $this->getTypeInModel($model, $type);
$comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, $type, true, null, $comment);
}
} elseif ($isAttribute) {
$name = Str::snake($method);
$types = $this->getAttributeReturnType($model, $method);
if ($types->has('get')) {
$type = $this->getTypeInModel($model, $types['get']);
$comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, $type, true, null, $comment);
}
if ($types->has('set')) {
$comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, null, null, true, $comment);
}
} elseif (
Str::startsWith($method, 'set') && Str::endsWith(
$method,
@@ -543,7 +609,6 @@ class ModelsCommand extends Command
//Magic set<name>Attribute
$name = Str::snake(substr($method, 3, -9));
if (!empty($name)) {
$reflection = new \ReflectionMethod($model, $method);
$comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, null, null, true, $comment);
}
@@ -551,7 +616,6 @@ class ModelsCommand extends Command
//Magic set<name>Attribute
$name = Str::camel(substr($method, 5));
if (!empty($name)) {
$reflection = new \ReflectionMethod($model, $method);
$comment = $this->getCommentFromDocBlock($reflection);
$args = $this->getParameters($reflection);
//Remove the first ($query) argument
@@ -582,8 +646,6 @@ class ModelsCommand extends Command
&& !Str::startsWith($method, 'get')
) {
//Use reflection to inspect the code, based on Illuminate/Support/SerializableClosure.php
$reflection = new \ReflectionMethod($model, $method);
if ($returnType = $reflection->getReturnType()) {
$type = $returnType instanceof ReflectionNamedType
? $returnType->getName()
@@ -714,6 +776,11 @@ class ModelsCommand extends Command
$fkProp = $reflectionObj->getProperty('foreignKey');
$fkProp->setAccessible(true);
if ($relation === 'belongsTo') {
return isset($this->nullableColumns[$fkProp->getValue($relationObj)]) ||
!in_array($fkProp->getValue($relationObj), $this->foreignKeyConstraintsColumns, true);
}
return isset($this->nullableColumns[$fkProp->getValue($relationObj)]);
}
@@ -749,7 +816,7 @@ class ModelsCommand extends Command
}
}
protected function setMethod($name, $type = '', $arguments = [], $comment = '')
public function setMethod($name, $type = '', $arguments = [], $comment = '')
{
$methods = array_change_key_case($this->methods, CASE_LOWER);
@@ -761,6 +828,18 @@ class ModelsCommand extends Command
}
}
public function unsetMethod($name)
{
unset($this->methods[strtolower($name)]);
}
public function getMethodType(Model $model, string $classType)
{
$modelName = $this->getClassNameInDestinationFile($model, get_class($model));
$builder = $this->getClassNameInDestinationFile($model, $classType);
return $builder . '|' . $modelName;
}
/**
* @param string $class
* @return string
@@ -879,6 +958,13 @@ class ModelsCommand extends Command
if (!$phpdocMixin->getText()) {
$mixinDocComment = preg_replace("/\s\*\s*\n/", '', $mixinDocComment);
}
foreach ($phpdoc->getTagsByName('mixin') as $tag) {
if (Str::startsWith($tag->getContent(), 'IdeHelper')) {
$phpdoc->deleteTag($tag);
}
}
$docComment = $serializer->getDocComment($phpdoc);
}
if ($this->write) {
@@ -900,11 +986,15 @@ class ModelsCommand extends Command
}
$classname = $this->write_mixin ? $mixinClassName : $classname;
$output = "namespace {$namespace}{\n{$docComment}\n\t{$keyword}class {$classname} extends \Eloquent ";
$output = "namespace {$namespace}{\n{$docComment}\n\t{$keyword}class {$classname} ";
if ($interfaceNames) {
$interfaces = implode(', \\', $interfaceNames);
$output .= "implements \\{$interfaces} ";
if (!$this->write_mixin) {
$output .= "extends \Eloquent ";
if ($interfaceNames) {
$interfaces = implode(', \\', $interfaceNames);
$output .= "implements \\{$interfaces} ";
}
}
return $output . "{}\n}\n\n";
@@ -923,7 +1013,8 @@ class ModelsCommand extends Command
$paramsWithDefault = [];
/** @var \ReflectionParameter $param */
foreach ($method->getParameters() as $param) {
$paramStr = '$' . $param->getName();
$paramStr = $param->isVariadic() ? '...$' . $param->getName() : '$' . $param->getName();
if ($paramType = $this->getParamType($method, $param)) {
$paramStr = $paramType . ' ' . $paramStr;
}
@@ -987,6 +1078,36 @@ class ModelsCommand extends Command
return $this->laravel['config']->get('ide-helper.model_camel_case_properties', false);
}
protected function getAttributeReturnType(Model $model, string $method): Collection
{
/** @var Attribute $attribute */
$attribute = $model->{$method}();
return collect([
'get' => $attribute->get ? optional(new \ReflectionFunction($attribute->get))->getReturnType() : null,
'set' => $attribute->set ? optional(new \ReflectionFunction($attribute->set))->getReturnType() : null,
])
->filter()
->map(function ($type) {
if ($type instanceof \ReflectionUnionType) {
$types =collect($type->getTypes())
/** @var ReflectionType $reflectionType */
->map(function ($reflectionType) {
return collect($this->extractReflectionTypes($reflectionType));
})
->flatten();
} else {
$types = collect($this->extractReflectionTypes($type));
}
if ($type->allowsNull()) {
$types->push('null');
}
return $types->join('|');
});
}
protected function getReturnType(\ReflectionMethod $reflection): ?string
{
$type = $this->getReturnTypeFromDocBlock($reflection);
@@ -1028,9 +1149,9 @@ class ModelsCommand extends Command
*
* @return null|string
*/
protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection)
protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection, \Reflector $reflectorForContext = null)
{
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
$phpDocContext = (new ContextFactory())->createFromReflector($reflectorForContext ?? $reflection);
$context = new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
@@ -1070,7 +1191,7 @@ class ModelsCommand extends Command
*/
protected function getSoftDeleteMethods($model)
{
$traits = class_uses(get_class($model), true);
$traits = class_uses_recursive($model);
if (in_array('Illuminate\\Database\\Eloquent\\SoftDeletes', $traits)) {
$modelName = $this->getClassNameInDestinationFile($model, get_class($model));
$builder = $this->getClassNameInDestinationFile($model, \Illuminate\Database\Query\Builder::class);
@@ -1091,18 +1212,18 @@ class ModelsCommand extends Command
return;
}
$traits = class_uses(get_class($model), true);
$modelName = get_class($model);
$traits = class_uses_recursive($modelName);
if (!in_array('Illuminate\\Database\\Eloquent\\Factories\\HasFactory', $traits)) {
return;
}
$modelName = get_class($model);
$modelBaseName = class_basename($modelName);
$factory = "\Database\Factories\\{$modelBaseName}Factory";
if ($modelName::newFactory()) {
$factory = get_class($modelName::newFactory());
} else {
$factory = Factory::resolveFactoryName($modelName);
}
$factory = '\\' . trim($factory, '\\');
@@ -1147,6 +1268,33 @@ class ModelsCommand extends Command
return $keyword;
}
protected function checkForCastableCasts(string $type, array $params = []): string
{
if (!class_exists($type) || !interface_exists(Castable::class)) {
return $type;
}
$reflection = new \ReflectionClass($type);
if (!$reflection->implementsInterface(Castable::class)) {
return $type;
}
$cast = call_user_func([$type, 'castUsing'], $params);
if (is_string($cast) && !is_object($cast)) {
return $cast;
}
$castReflection = new ReflectionObject($cast);
$methodReflection = $castReflection->getMethod('get');
return $this->getReturnTypeFromReflection($methodReflection) ??
$this->getReturnTypeFromDocBlock($methodReflection, $reflection) ??
'mixed';
}
/**
* @param string $type
* @return string|null
@@ -1200,7 +1348,7 @@ class ModelsCommand extends Command
;
$className = trim($className, '\\');
$writingToExternalFile = !$this->write;
$writingToExternalFile = !$this->write || $this->write_mixin;
$classIsNotInExternalFile = $reflection->getName() !== $className;
$forceFQCN = $this->laravel['config']->get('ide-helper.force_fqn', false);
@@ -1283,7 +1431,7 @@ class ModelsCommand extends Command
$docComment ?? '',
$matches
);
$type = $matches[1] ?? null;
$type = $matches[1] ?? '';
if (strpos($type, '|') !== false) {
$types = explode('|', $type);
@@ -1384,4 +1532,18 @@ class ModelsCommand extends Command
$hookInstance->run($this, $model);
}
}
/**
* @param \Doctrine\DBAL\Schema\AbstractSchemaManager $schema
* @param string $table
* @throws DBALException
*/
protected function setForeignKeys($schema, $table)
{
foreach ($schema->listTableForeignKeys($table) as $foreignKeyConstraint) {
foreach ($foreignKeyConstraint->getLocalColumns() as $columnName) {
$this->foreignKeyConstraintsColumns[] = $columnName;
}
}
}
}
+1 -1
View File
@@ -296,7 +296,7 @@ class Generator
return !$reflection->isInternal() && $reflection->getName() === $class;
})
->filter(function ($class) {
$traits = class_uses($class);
$traits = class_uses_recursive($class);
// Filter only classes with the macroable trait
return isset($traits[Macroable::class]);
+33 -5
View File
@@ -42,8 +42,16 @@ class Macro extends Method
// Add macro parameters if they are missed in original docblock
if (!$this->phpdoc->hasTag('param')) {
foreach ($method->getParameters() as $parameter) {
$type = $parameter->hasType() ? $parameter->getType()->getName() : 'mixed';
$type .= $parameter->hasType() && $parameter->getType()->allowsNull() ? '|null' : '';
$reflectionType = $parameter->getType();
$type = $this->concatReflectionTypes($reflectionType);
/** @psalm-suppress UndefinedClass */
if ($reflectionType && !$reflectionType instanceof \ReflectionUnionType && $reflectionType->allowsNull()) {
$type .= '|null';
}
$type = $type ?: 'mixed';
$name = $parameter->isVariadic() ? '...' : '';
$name .= '$' . $parameter->getName();
@@ -57,14 +65,31 @@ class Macro extends Method
$builder = EloquentBuilder::class;
$return = $method->getReturnType();
$type = $return->getName();
$type .= $this->root === "\\{$builder}" && $return->getName() === $builder ? '|static' : '';
$type .= $return->allowsNull() ? '|null' : '';
$type = $this->concatReflectionTypes($return);
/** @psalm-suppress UndefinedClass */
if (!$return instanceof \ReflectionUnionType) {
$type .= $this->root === "\\{$builder}" && $return->getName() === $builder ? '|static' : '';
$type .= $return->allowsNull() ? '|null' : '';
}
$this->phpdoc->appendTag(Tag::createInstance("@return {$type}"));
}
}
protected function concatReflectionTypes(?\ReflectionType $type): string
{
/** @psalm-suppress UndefinedClass */
$returnTypes = $type instanceof \ReflectionUnionType
? $type->getTypes()
: [$type];
return Collection::make($returnTypes)
->filter()
->map->getName()
->implode('|');
}
protected function addLocationToPhpDoc()
{
if ($this->method->name === '__invoke') {
@@ -73,6 +98,9 @@ class Macro extends Method
$enclosingClass = $this->method->getClosureScopeClass();
}
if (!$enclosingClass) {
return;
}
/** @var \ReflectionMethod $enclosingMethod */
$enclosingMethod = Collection::make($enclosingClass->getMethods())
->first(function (\ReflectionMethod $method) {
+6 -3
View File
@@ -45,12 +45,15 @@ class EloquentCommandTest extends TestCase
$this->assertMatchesTxtSnapshot($actualContent);
$display = $tester->getDisplay();
$this->assertRegExp(
$this->assertMatchesRegularExpression(
';Unexpected no document on Illuminate\\\Database\\\Eloquent\\\Model;',
$display
);
$this->assertRegExp(
';Wrote expected docblock to .*/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php;',
$modelClassFilePath = preg_quote(
str_replace('/', DIRECTORY_SEPARATOR, '/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php')
);
$this->assertMatchesRegularExpression(
';Wrote expected docblock to .*' . $modelClassFilePath . ';',
$display
);
}
@@ -8,6 +8,7 @@ use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
use Barryvdh\LaravelIdeHelper\Tests\TestCase;
use Illuminate\Filesystem\Filesystem;
use Mockery\MockInterface;
use stdClass;
class MetaCommandTest extends TestCase
{
@@ -37,6 +38,38 @@ class MetaCommandTest extends TestCase
self::assertStringContainsString('override(', $this->mockFilesystemOutput);
}
public function testUnregisterAutoloader(): void
{
$current = spl_autoload_functions();
$appended = function () {
};
$this->app->bind('registers-autoloader', function () use ($appended) {
spl_autoload_register($appended);
return new stdClass();
});
$this->mockFilesystem();
/** @var Filesystem|MockInterface $mockFileSystem */
$mockFileSystem = $this->app->make(Filesystem::class);
$this->instance('files', $mockFileSystem);
$mockFileSystem
->shouldReceive('getRequire')
->andReturnUsing(function ($__path, $__data) {
return (static function () use ($__path, $__data) {
extract($__data, EXTR_SKIP);
return require $__path;
})();
});
$this->artisan('ide-helper:meta');
self::assertSame(array_merge($current, [$appended]), spl_autoload_functions());
}
/**
* Get package providers.
*
@@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Attributes\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
class Simple extends Model
{
public function name(): Attribute
{
return new Attribute(
function (?string $name): ?string {
return $name;
},
function (?string $name): ?string {
return $name === null ? null : ucfirst($name);
}
);
}
/**
* ide-helper does not recognize this method being an Attribute
* because the method has no actual return type;
* phpdoc is ignored here deliberately due to performance reasons and also
* isn't supported by Laravel itself.
*
* @return \Illuminate\Database\Eloquent\Casts\Attribute
*/
public function notAnAttribute()
{
return new Attribute(
function (?string $value): ?string {
return $value;
},
function (?string $value): ?string {
return $value;
}
);
}
}
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Attributes;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
protected function setUp(): void
{
parent::setUp();
if (!class_exists('\Illuminate\Database\Eloquent\Casts\Attribute')) {
$this->markTestSkipped('This test requires Laravel 8.77 or newer');
}
}
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,54 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Attributes\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Attributes\Models\Simple
*
* @property integer $id
* @property string|null $name
* @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple query()
* @method static \Illuminate\Database\Eloquent\Builder|Simple whereId($value)
* @mixin \Eloquent
*/
class Simple extends Model
{
public function name(): Attribute
{
return new Attribute(
function (?string $name): ?string {
return $name;
},
function (?string $name): ?string {
return $name === null ? null : ucfirst($name);
}
);
}
/**
* ide-helper does not recognize this method being an Attribute
* because the method has no actual return type;
* phpdoc is ignored here deliberately due to performance reasons and also
* isn't supported by Laravel itself.
*
* @return \Illuminate\Database\Eloquent\Casts\Attribute
*/
public function notAnAttribute()
{
return new Attribute(
function (?string $value): ?string {
return $value;
},
function (?string $value): ?string {
return $value;
}
);
}
}
@@ -18,22 +18,24 @@ class Test extends AbstractModelsCommand
]);
if (PHP_VERSION_ID >= 80000) {
$errors = <<<TXT
Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicBelongsTo() : Attempt to read property "created_at" on null
Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasMany() : Attempt to read property "created_at" on null
Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasOne() : Attempt to read property "created_at" on null
TXT;
$errors = [
'Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicBelongsTo() : Attempt to read property "created_at" on null',
'Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasMany() : Attempt to read property "created_at" on null',
'Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasOne() : Attempt to read property "created_at" on null',
];
} else {
$errors = <<<TXT
Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicBelongsTo() : Trying to get property 'created_at' of non-object
Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasMany() : Trying to get property 'created_at' of non-object
Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasOne() : Trying to get property 'created_at' of non-object
TXT;
$errors = [
"Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicBelongsTo() : Trying to get property 'created_at' of non-object",
"Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasMany() : Trying to get property 'created_at' of non-object",
"Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasOne() : Trying to get property 'created_at' of non-object",
];
}
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertStringContainsString($errors, $tester->getDisplay());
foreach ($errors as $error) {
$this->assertStringContainsString($error, $tester->getDisplay());
}
$this->assertMatchesMockedSnapshot();
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\CustomSpace;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models\ModelWithCustomNamespace;
use Illuminate\Database\Eloquent\Factories\Factory;
class ModelWithCustomNamespaceFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = ModelWithCustomNamespace::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
//
];
}
}
@@ -4,17 +4,17 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Getter\Models\Simple;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models\ModelWithFactory;
use Illuminate\Database\Eloquent\Factories\Factory;
class SimpleFactory extends Factory
class ModelWithFactoryFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Simple::class;
protected $model = ModelWithFactory::class;
/**
* Define the model's default state.
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models\ModelWithNestedFactory;
use Illuminate\Database\Eloquent\Factories\Factory;
class ModelWithNestedFactoryFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = ModelWithNestedFactory::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
//
];
}
}
@@ -4,11 +4,11 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories\SimpleFactory;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\CustomSpace\ModelWithCustomNamespaceFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Simple extends Model
class ModelWithCustomNamespace extends Model
{
use HasFactory;
@@ -19,6 +19,6 @@ class Simple extends Model
*/
protected static function newFactory()
{
return SimpleFactory::new();
return ModelWithCustomNamespaceFactory::new();
}
}
@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ModelWithFactory extends Model
{
use HasFactory;
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
class ModelWithNestedFactory extends ModelWithFactory
{
}
@@ -6,7 +6,10 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
use Closure;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Foundation\Application;
use Illuminate\Support\Str;
class Test extends AbstractModelsCommand
{
@@ -18,6 +21,8 @@ class Test extends AbstractModelsCommand
);
}
Factory::guessFactoryNamesUsing(static::getFactoryNameResolver());
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
@@ -29,4 +34,19 @@ class Test extends AbstractModelsCommand
$this->assertStringNotContainsString('not found', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
public static function getFactoryNameResolver(): Closure
{
// This mimics the default resolver, but with adjusted test namespaces.
// Illuminate\Database\Eloquent\Factories\Factory::resolveFactoryName
return function (string $modelName): string {
$appNamespace = 'Barryvdh\\LaravelIdeHelper\\Tests\\Console\\ModelsCommand\\Factories\\';
$modelName = Str::startsWith($modelName, $appNamespace . 'Models\\')
? Str::after($modelName, $appNamespace . 'Models\\')
: Str::after($modelName, $appNamespace);
return $appNamespace . 'Factories\\' . $modelName . 'Factory';
};
}
}
@@ -4,6 +4,79 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\CustomSpace\ModelWithCustomNamespaceFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models\ModelWithCustomNamespace
*
* @method static \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\CustomSpace\ModelWithCustomNamespaceFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithCustomNamespace newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithCustomNamespace newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithCustomNamespace query()
* @mixin \Eloquent
*/
class ModelWithCustomNamespace extends Model
{
use HasFactory;
/**
* Create a new factory instance for the model.
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
protected static function newFactory()
{
return ModelWithCustomNamespaceFactory::new();
}
}
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models\ModelWithFactory
*
* @method static \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories\ModelWithFactoryFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithFactory newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithFactory newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithFactory query()
* @mixin \Eloquent
*/
class ModelWithFactory extends Model
{
use HasFactory;
}
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models\ModelWithNestedFactory
*
* @method static \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories\ModelWithNestedFactoryFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithNestedFactory newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithNestedFactory newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithNestedFactory query()
* @mixin \Eloquent
*/
class ModelWithNestedFactory extends ModelWithFactory
{
}
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
@@ -19,38 +92,3 @@ class ModelWithoutFactory extends Model
{
use HasFactory;
}
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories\SimpleFactory;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models\Simple
*
* @property integer $id
* @method static \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories\SimpleFactory factory(...$parameters)
* @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple query()
* @method static \Illuminate\Database\Eloquent\Builder|Simple whereId($value)
* @mixin \Eloquent
*/
class Simple extends Model
{
use HasFactory;
/**
* Create a new factory instance for the model.
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
protected static function newFactory()
{
return SimpleFactory::new();
}
}
@@ -183,6 +183,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWi
* @method static \Illuminate\Database\Eloquent\Builder|Post whereYearNullable($value)
* @mixin \Eloquent
*/
class IdeHelperPost extends \Eloquent {}
class IdeHelperPost {}
}
@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class CastableReturnsAnonymousCaster implements Castable
{
public static function castUsing(array $arguments)
{
return new class() implements CastsAttributes {
/**
* @inheritDoc
* @return CastedProperty
*/
public function get($model, string $key, $value, array $attributes)
{
return new CastedProperty();
}
/**
* @inheritDoc
*/
public function set($model, string $key, $value, array $attributes)
{
// TODO: Implement set() method.
}
};
}
}
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts;
use Illuminate\Contracts\Database\Eloquent\Castable;
class CastableReturnsCustomCaster implements Castable
{
public static function castUsing(array $arguments)
{
return CustomCasterWithDocblockReturn::class;
}
}
@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class CastableWithoutReturnType implements Castable
{
public static function castUsing(array $arguments)
{
return new class() implements CastsAttributes {
public function get($model, string $key, $value, array $attributes)
{
return new CastedProperty();
}
public function set($model, string $key, $value, array $attributes)
{
// TODO: Implement set() method.
}
};
}
}
@@ -4,6 +4,9 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableReturnsAnonymousCaster;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableReturnsCustomCaster;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableWithoutReturnType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithDocblockReturn;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithDocblockReturnFqn;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithNullablePrimitiveReturn;
@@ -31,8 +34,12 @@ class CustomCast extends Model
'casted_property_with_param' => CustomCasterWithParam::class . ':param',
'casted_property_with_static_return_docblock' => SelfCastingCasterWithStaticDocblockReturn::class,
'casted_property_with_this_return_docblock' => SelfCastingCasterWithThisDocblockReturn::class,
'casted_property_with_castable' => CastableReturnsCustomCaster::class,
'casted_property_with_anonymous_cast' => CastableReturnsAnonymousCaster::class,
'casted_property_without_return_type' => CastableWithoutReturnType::class,
'extended_casted_property_with_static_return_docblock' => ExtendedSelfCastingCasterWithStaticDocblockReturn::class,
'extended_casted_property_with_this_return_docblock' => ExtendedSelfCastingCasterWithThisDocblockReturn::class,
'casted_property_with_static_return_docblock_and_param' => SelfCastingCasterWithStaticDocblockReturn::class . ':param',
'cast_without_property' => CustomCasterWithReturnType::class,
];
}
@@ -4,6 +4,9 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableReturnsAnonymousCaster;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableReturnsCustomCaster;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableWithoutReturnType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithDocblockReturn;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithDocblockReturnFqn;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithNullablePrimitiveReturn;
@@ -34,6 +37,10 @@ use Illuminate\Database\Eloquent\Model;
* @property ExtendedSelfCastingCasterWithStaticDocblockReturn $extended_casted_property_with_static_return_docblock
* @property ExtendedSelfCastingCasterWithThisDocblockReturn $extended_casted_property_with_this_return_docblock
* @property SelfCastingCasterWithStaticDocblockReturn $casted_property_with_static_return_docblock_and_param
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_castable
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_anonymous_cast
* @property mixed $casted_property_without_return_type
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $cast_without_property
* @method static \Illuminate\Database\Eloquent\Builder|CustomCast newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|CustomCast newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|CustomCast query()
@@ -65,8 +72,12 @@ class CustomCast extends Model
'casted_property_with_param' => CustomCasterWithParam::class . ':param',
'casted_property_with_static_return_docblock' => SelfCastingCasterWithStaticDocblockReturn::class,
'casted_property_with_this_return_docblock' => SelfCastingCasterWithThisDocblockReturn::class,
'casted_property_with_castable' => CastableReturnsCustomCaster::class,
'casted_property_with_anonymous_cast' => CastableReturnsAnonymousCaster::class,
'casted_property_without_return_type' => CastableWithoutReturnType::class,
'extended_casted_property_with_static_return_docblock' => ExtendedSelfCastingCasterWithStaticDocblockReturn::class,
'extended_casted_property_with_this_return_docblock' => ExtendedSelfCastingCasterWithThisDocblockReturn::class,
'casted_property_with_static_return_docblock_and_param' => SelfCastingCasterWithStaticDocblockReturn::class . ':param',
'cast_without_property' => CustomCasterWithReturnType::class,
];
}
@@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class CustomMethod implements ModelHookInterface
{
public function run(ModelsCommand $command, Model $model): void
{
$command->setMethod('custom', $command->getMethodType($model, Builder::class), ['$custom']);
}
}
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface;
use Illuminate\Database\Eloquent\Model;
class UnsetMethod implements ModelHookInterface
{
public function run(ModelsCommand $command, Model $model): void
{
$command->unsetMethod('query');
}
}
@@ -6,7 +6,9 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks\CustomMethod;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks\CustomProperty;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks\UnsetMethod;
use Illuminate\Filesystem\Filesystem;
use Mockery;
@@ -24,6 +26,8 @@ class Test extends AbstractModelsCommand
],
'model_hooks' => [
CustomProperty::class,
CustomMethod::class,
UnsetMethod::class,
],
]);
}
@@ -71,9 +75,9 @@ use Illuminate\Database\Eloquent\Model;
*
* @property int $id
* @property-read string $custom
* @method static \Illuminate\Database\Eloquent\Builder|Simple custom($custom)
* @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple query()
* @method static \Illuminate\Database\Eloquent\Builder|Simple whereId($value)
* @mixin \Eloquent
*/
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class BelongsToVariation extends Model
{
public function notNullColumnWithForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(self::class, 'not_null_column_with_foreign_key_constraint');
}
public function notNullColumnWithNoForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(self::class, 'not_null_column_with_no_foreign_key_constraint');
}
public function nullableColumnWithForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(self::class, 'nullable_column_with_foreign_key_constraint');
}
public function nullableColumnWithNoForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(self::class, 'nullable_column_with_no_foreign_key_constraint');
}
}
@@ -4,6 +4,59 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Models\BelongsToVariation
*
* @property integer $id
* @property integer $not_null_column_with_foreign_key_constraint
* @property integer $not_null_column_with_no_foreign_key_constraint
* @property integer|null $nullable_column_with_foreign_key_constraint
* @property integer|null $nullable_column_with_no_foreign_key_constraint
* @property-read BelongsToVariation $notNullColumnWithForeignKeyConstraint
* @property-read BelongsToVariation|null $notNullColumnWithNoForeignKeyConstraint
* @property-read BelongsToVariation|null $nullableColumnWithForeignKeyConstraint
* @property-read BelongsToVariation|null $nullableColumnWithNoForeignKeyConstraint
* @method static \Illuminate\Database\Eloquent\Builder|BelongsToVariation newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|BelongsToVariation newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|BelongsToVariation query()
* @method static \Illuminate\Database\Eloquent\Builder|BelongsToVariation whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|BelongsToVariation whereNotNullColumnWithForeignKeyConstraint($value)
* @method static \Illuminate\Database\Eloquent\Builder|BelongsToVariation whereNotNullColumnWithNoForeignKeyConstraint($value)
* @method static \Illuminate\Database\Eloquent\Builder|BelongsToVariation whereNullableColumnWithForeignKeyConstraint($value)
* @method static \Illuminate\Database\Eloquent\Builder|BelongsToVariation whereNullableColumnWithNoForeignKeyConstraint($value)
* @mixin \Eloquent
*/
class BelongsToVariation extends Model
{
public function notNullColumnWithForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(self::class, 'not_null_column_with_foreign_key_constraint');
}
public function notNullColumnWithNoForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(self::class, 'not_null_column_with_no_foreign_key_constraint');
}
public function nullableColumnWithForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(self::class, 'nullable_column_with_foreign_key_constraint');
}
public function nullableColumnWithNoForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(self::class, 'nullable_column_with_no_foreign_key_constraint');
}
}
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\ModelsOtherNamespace\AnotherModel;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Traits\HasTestRelations;
use Illuminate\Database\Eloquent\Model;
@@ -20,15 +73,15 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Models\Simple
*
* @property integer $id
* @property-read Simple $relationBelongsTo
* @property-read AnotherModel $relationBelongsToInAnotherNamespace
* @property-read Simple|null $relationBelongsTo
* @property-read AnotherModel|null $relationBelongsToInAnotherNamespace
* @property-read \Illuminate\Database\Eloquent\Collection|Simple[] $relationBelongsToMany
* @property-read int|null $relation_belongs_to_many_count
* @property-read \Illuminate\Database\Eloquent\Collection|Simple[] $relationBelongsToManyWithSub
* @property-read int|null $relation_belongs_to_many_with_sub_count
* @property-read \Illuminate\Database\Eloquent\Collection|Simple[] $relationBelongsToManyWithSubAnother
* @property-read int|null $relation_belongs_to_many_with_sub_another_count
* @property-read AnotherModel $relationBelongsToSameNameAsColumn
* @property-read AnotherModel|null $relationBelongsToSameNameAsColumn
* @property-read \Illuminate\Database\Eloquent\Collection|Simple[] $relationHasMany
* @property-read int|null $relation_has_many_count
* @property-read Simple|null $relationHasOne
@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Models;
use Illuminate\Database\Eloquent\Model;
class SimpleCast extends Model
{
protected $casts = [
'cast_to_int' => 'int',
'cast_to_integer' => 'integer',
'cast_to_real' => 'real',
'cast_to_float' => 'float',
'cast_to_double' => 'double',
'cast_to_decimal' => 'decimal:4',
'cast_to_string' => 'string',
'cast_to_bool' => 'bool',
'cast_to_boolean' => 'boolean',
'cast_to_object' => 'object',
'cast_to_array' => 'array',
'cast_to_json' => 'json',
'cast_to_collection' => 'collection',
'cast_to_date' => 'date',
'cast_to_datetime' => 'datetime',
'cast_to_custom_datetime' => 'custom_datetime:Y-m-d H:i:s',
'cast_to_immutable_date' => 'immutable_date',
'cast_to_immutable_custom_datetime' => 'immutable_custom_datetime:Y-m-d H:i:s',
'cast_to_immutable_datetime' => 'immutable_datetime',
'cast_to_timestamp' => 'timestamp',
'cast_to_encrypted' => 'encrypted',
'cast_to_encrypted_array' => 'encrypted:array',
'cast_to_encrypted_collection' => 'encrypted:collection',
'cast_to_encrypted_json' => 'encrypted:json',
'cast_to_encrypted_object' => 'encrypted:object',
];
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts;
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,96 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SimpleCasts\Models\SimpleCast
*
* @property integer $cast_to_int
* @property integer $cast_to_integer
* @property float $cast_to_real
* @property float $cast_to_float
* @property float $cast_to_double
* @property string $cast_to_decimal
* @property string $cast_to_string
* @property boolean $cast_to_bool
* @property boolean $cast_to_boolean
* @property object $cast_to_object
* @property array $cast_to_array
* @property array $cast_to_json
* @property \Illuminate\Support\Collection $cast_to_collection
* @property \Illuminate\Support\Carbon $cast_to_date
* @property \Illuminate\Support\Carbon $cast_to_datetime
* @property \Illuminate\Support\Carbon $cast_to_custom_datetime
* @property \Carbon\CarbonImmutable $cast_to_immutable_date
* @property \Carbon\CarbonImmutable $cast_to_immutable_custom_datetime
* @property \Carbon\CarbonImmutable $cast_to_immutable_datetime
* @property integer $cast_to_timestamp
* @property mixed $cast_to_encrypted
* @property array $cast_to_encrypted_array
* @property \Illuminate\Support\Collection $cast_to_encrypted_collection
* @property array $cast_to_encrypted_json
* @property object $cast_to_encrypted_object
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast query()
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToArray($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToBool($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToBoolean($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToCollection($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToCustomDatetime($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToDate($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToDatetime($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToDecimal($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToDouble($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToEncrypted($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToEncryptedArray($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToEncryptedCollection($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToEncryptedJson($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToEncryptedObject($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToFloat($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToImmutableCustomDatetime($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToImmutableDate($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToImmutableDatetime($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToInt($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToInteger($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToJson($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToObject($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToReal($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToString($value)
* @method static \Illuminate\Database\Eloquent\Builder|SimpleCast whereCastToTimestamp($value)
* @mixin \Eloquent
*/
class SimpleCast extends Model
{
protected $casts = [
'cast_to_int' => 'int',
'cast_to_integer' => 'integer',
'cast_to_real' => 'real',
'cast_to_float' => 'float',
'cast_to_double' => 'double',
'cast_to_decimal' => 'decimal:4',
'cast_to_string' => 'string',
'cast_to_bool' => 'bool',
'cast_to_boolean' => 'boolean',
'cast_to_object' => 'object',
'cast_to_array' => 'array',
'cast_to_json' => 'json',
'cast_to_collection' => 'collection',
'cast_to_date' => 'date',
'cast_to_datetime' => 'datetime',
'cast_to_custom_datetime' => 'custom_datetime:Y-m-d H:i:s',
'cast_to_immutable_date' => 'immutable_date',
'cast_to_immutable_custom_datetime' => 'immutable_custom_datetime:Y-m-d H:i:s',
'cast_to_immutable_datetime' => 'immutable_datetime',
'cast_to_timestamp' => 'timestamp',
'cast_to_encrypted' => 'encrypted',
'cast_to_encrypted_array' => 'encrypted:array',
'cast_to_encrypted_collection' => 'encrypted:collection',
'cast_to_encrypted_json' => 'encrypted:json',
'cast_to_encrypted_object' => 'encrypted:object',
];
}
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Variadic\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class Simple extends Model
{
public function scopeWhereVariadic(Builder $query, ...$values): void
{
}
public function scopeWhereTypedVariadic(Builder $query, int ...$values): void
{
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Variadic;
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,31 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Variadic\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Variadic\Models\Simple
*
* @property integer $id
* @method static Builder|Simple newModelQuery()
* @method static Builder|Simple newQuery()
* @method static Builder|Simple query()
* @method static Builder|Simple whereId($value)
* @method static Builder|Simple whereTypedVariadic(int ...$values)
* @method static Builder|Simple whereVariadic(...$values)
* @mixin \Eloquent
*/
class Simple extends Model
{
public function scopeWhereVariadic(Builder $query, ...$values): void
{
}
public function scopeWhereTypedVariadic(Builder $query, int ...$values): void
{
}
}
@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class BelongsToVariationTable extends Migration
{
public function up(): void
{
Schema::create('belongs_to_variations', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('not_null_column_with_foreign_key_constraint');
$table->integer('not_null_column_with_no_foreign_key_constraint');
$table->integer('nullable_column_with_foreign_key_constraint')->nullable();
$table->integer('nullable_column_with_no_foreign_key_constraint')->nullable();
$table->foreign('not_null_column_with_foreign_key_constraint')->references('id')->on('belongs_to_variations');
$table->foreign('nullable_column_with_foreign_key_constraint')->references('id')->on('belongs_to_variations');
});
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class SimpleCastsTable extends Migration
{
public function up(): void
{
Schema::create('simple_casts', static function (Blueprint $table) {
$table->string('cast_to_int');
$table->string('cast_to_integer');
$table->string('cast_to_real');
$table->string('cast_to_float');
$table->string('cast_to_double');
$table->string('cast_to_decimal');
$table->string('cast_to_string');
$table->string('cast_to_bool');
$table->string('cast_to_boolean');
$table->string('cast_to_object');
$table->string('cast_to_array');
$table->string('cast_to_json');
$table->string('cast_to_collection');
$table->string('cast_to_date');
$table->string('cast_to_datetime');
$table->string('cast_to_custom_datetime');
$table->string('cast_to_immutable_date');
$table->string('cast_to_immutable_custom_datetime');
$table->string('cast_to_immutable_datetime');
$table->string('cast_to_timestamp');
$table->string('cast_to_encrypted');
$table->string('cast_to_encrypted_array');
$table->string('cast_to_encrypted_collection');
$table->string('cast_to_encrypted_json');
$table->string('cast_to_encrypted_object');
});
}
}
+23
View File
@@ -188,6 +188,29 @@ class MacroTest extends TestCase
$this->assertEquals('@return \stdClass|null rrrrrrr', $this->tagsToString($phpdoc, 'return'));
}
public function testInitPhpDocParamsWithUnionTypes(): void
{
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('This test requires PHP 8.0 or higher');
}
$phpdoc = (new MacroMock())->getPhpDoc(eval(<<<'PHP'
return new ReflectionFunction(
/**
* Test docblock.
*/
function (\Stringable|string $a = null): \Stringable|string|null {
return $a;
}
);
PHP));
$this->assertNotNull($phpdoc);
$this->assertStringContainsString('Test docblock', $phpdoc->getText());
$this->assertEquals('@param \Stringable|string|null $a', $this->tagsToString($phpdoc, 'param'));
$this->assertEquals('@return \Stringable|string|null', $this->tagsToString($phpdoc, 'return'));
}
protected function tagsToString(DocBlock $docBlock, string $name)
{
$tags = $docBlock->getTagsByName($name);