Compare commits

...
29 Commits
Author SHA1 Message Date
Markus Podar bcc233280b [TEST] Show that custom phpdoc tags without space afterwards are unexpectedly changed (#981)
* tests: show that custom phpdoc tags without space afterwards are unexpectedly changed

This adds a test for the (current bug) behaviour of https://github.com/barryvdh/laravel-ide-helper/issues/666

If this ever gets fixed, this fails and needs to be adapted,
ensuring it won't break again.

* test: adapt now that #666 has been fixed
2024-02-16 21:38:43 +01:00
Barry vd. Heuvel 77831852bb Fix final class keyword (#1517)
* Fix final class keyword

* Add changelog
2024-02-15 15:23:20 +01:00
Markus Podar b47a118300 Adjust gitattribute (#1515)
and make up for the missing php-cs-fixer changes

The rest of the changes is just:
- sorted alphabetically
- align the `export-ignore` column
2024-02-14 14:49:24 +01:00
Barry vd. HeuvelandMarkus Podar dca3ebe81e Prepare 2.15 release (#1514)
* Prepare 2.15 release

* Prepare 2.15 release

* Add missing CHANGELOG.md entries

---------

Co-authored-by: Markus Podar <[email protected]>
2024-02-14 12:19:26 +01:00
Barry vd. Heuvel c119e89cf0 CS 2024-02-08 11:11:51 +01:00
GeoSot bc158a1b86 Add AllowDynamicProperties Attribute to cooperate with php8.2 deprecation (#1428)
* Add `AllowDynamicProperties` Attribute to cooperate with php8.2 deprecation

* lint snapshot
2024-02-08 11:09:30 +01:00
341c0ab89f feat: add support for AsCollection and AsArrayObject casts (#1393)
* feat: add support for AsCollection and AsArrayObject casts

* update changelog

* improve tests

* remove change

* composer fix-style

* snapshot

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
Co-authored-by: laravel-ide-helper <[email protected]>
2024-02-08 10:59:02 +01:00
Menthol cee441c87c Fix #1300 relation_return_type must take precedence if it is defined (#1394) 2024-02-08 09:48:26 +01:00
Barry vd. Heuvel 9343d3a1cc CS 2024-02-08 09:41:24 +01:00
f12d933ab1 Add support for non type-hinted attribute accessors with no backed property (#1411)
* Read Attribute type from parameter

* Update CHANGELOG.md

* Update ModelsCommand.php

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
Co-authored-by: Barry vd. Heuvel <[email protected]>
2024-02-08 09:39:10 +01:00
Norman Huth 4751420c9e Accept scope & scopes as relation (#1452) 2024-02-08 09:20:47 +01:00
Caleb White 6115100a0f Add support for composite keys (#1479)
* Add support for composite keys

* chore: add composite foreign key test
2024-02-08 07:42:27 +01:00
Sven 5c886fb653 Support the BINARY(...) database field type (#1434)
In singlestore we define some strings as
$table->char($columnName, 40)->charset('binary')->collation('binary');

In singelstore, this becomes 'BINARY(40)' instead of 'CHAR(40) CHARACTER SET 'binary' COLLATE 'binary'

This change would allow the generated proeperties to be 'string' instead of 'mixed', as using a string is correct in this case.
2024-02-07 22:01:48 +01:00
Barry vd. Heuvel 7d69dc6ea0 CS 2024-02-07 21:58:39 +01:00
Ben Poulson 62fc436c4c Reintroduce support for multi-db setups (#1426)
This snipet was lost in the 2.12.x => 2.13.x change. Needs to be reintroduced to allow those who use fully qualified db+table names. (Eg, `database.table`)
2024-02-07 21:55:16 +01:00
409bbf665b Fix unsetMethod in ModelsCommand (#1453)
* Fix unsetMethod in ModelsCommand

* composer fix-style

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
Co-authored-by: laravel-ide-helper <[email protected]>
2024-02-07 21:48:09 +01:00
Barry vd. Heuvel 2535f8e6ab Fix tests and CS 2024-02-07 21:44:48 +01:00
James Bhatta 16548fc878 Update README.md (#1451)
Fix intra page link for PHPDocs for models
2024-02-07 21:40:24 +01:00
Barry vd. Heuvel 872b470d9b Update CHANGELOG.md 2024-02-07 21:37:51 +01:00
Filip Iulian Pacurar 04c1a8d444 Support generating helper files for real-time facades (#1455)
* Support generating helper files for real-time facades

Add changelog entry for real-time facades support

Mention real-time facades support in readme

Fix code style

Import

* Remove useless assertions

We cannot 100% control the output file has only the real-time facades, anything else can be in the final output
2024-02-07 21:35:02 +01:00
snmatsui c290af858d reset foreignKeyConstraintsColumns on model loop start (#1461) 2024-02-07 21:31:12 +01:00
f64ff9c0b1 Add support for enum default arguments using enum cases (#1464)
* add case when default value is of type enum

* Update CHANGELOG.md

* add tests for enum arguments default values

* ignore psalm issue (built-in `\UnitEnum` class does not exists in PHP7)

* Update run-static-analysis.yml to use PHP8.1

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
Co-authored-by: Barry vd. Heuvel <[email protected]>
2024-02-07 21:27:30 +01:00
Allan LaalandBarry vd. Heuvel f3ad134242 FIX vulnerability CVE-2021-43608 (#1392)
Doctrine DBAL 3.x before 3.1.4 allows SQL Injection: https://www.cve.org/CVERecord?id=CVE-2021-43608

Co-authored-by: Barry vd. Heuvel <[email protected]>
2024-02-07 21:24:42 +01:00
PapaBearandTimo Frenzel 9d14b019f6 skip swoole, otherwise fatal error (#1477)
Co-authored-by: Timo Frenzel <[email protected]>
2024-02-07 21:19:43 +01:00
netpok f4656256fc Fix non-facade classes will result in no autocomplete (#841)
* Filter out non-facade base classes

* Remove Model as it is already skipped because it's not a facade
2024-02-07 21:06:31 +01:00
Jackey Cheung cc9ed4423b disable inspections of helper files (#1486)
* disables all jetbrains inspections

* disables all jetbrains' & phpcs' inspections
2024-02-07 19:30:35 +01:00
Markus Podar 512612f22f Remove outdated dev references to older versions (#1508) 2024-02-07 19:29:10 +01:00
Toby TwiggerandToby Twigger 115068e57b Clarify use of --reset and --smart-reset flags in documentation (#1494)
* Update README.md

Clarified model behaviour when using the --reset option

* Add --smart-reset to the documentation

---------

Co-authored-by: Toby Twigger <[email protected]>
2024-02-07 14:36:45 +01:00
Markus Podar a8b34ad228 Removed support for Laravel 8 and therefore for PHP < 8.0 (#1504)
* Removed support for Laravel 8 and therefore for PHP < 8.0

* Update php-cs-fixer to v3

* composer fix-style
2024-02-05 13:25:08 +01:00
57 changed files with 1274 additions and 320 deletions
+11 -11
View File
@@ -2,14 +2,14 @@
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html # https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
# Ignore all test and documentation with "export-ignore". # Ignore all test and documentation with "export-ignore".
/.gitattributes export-ignore /.editorconfig export-ignore
/.github export-ignore /.gitattributes export-ignore
/.gitignore export-ignore /.github export-ignore
/.editorconfig export-ignore /.gitignore export-ignore
/phpunit.xml.dist export-ignore /.php-cs-fixer.cache export-ignore
/tests export-ignore /.php-cs-fixer.php export-ignore
/.php_cs.common.php export-ignore /.php-cs-fixer.tests.cache export-ignore
/.php_cs.dist export-ignore /phpunit.xml.dist export-ignore
/.php_cs.tests.php export-ignore /psalm-baseline.xml export-ignore
/psalm.xml export-ignore /psalm.xml export-ignore
/psalm-baseline.xml export-ignore /tests export-ignore
+2 -14
View File
@@ -19,25 +19,13 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: [8.3, 8.2, 8.1, 8.0, 7.4, 7.3] php: [8.3, 8.2, 8.1, 8.0]
laravel: [10.*, 9.*, 8.*] laravel: [10.*, 9.*]
exclude: exclude:
- php: 8.0 - php: 8.0
laravel: 10.* laravel: 10.*
- php: 7.4
laravel: 10.*
- php: 7.3
laravel: 10.*
- php: 8.3 - php: 8.3
laravel: 9.* laravel: 9.*
- php: 7.4
laravel: 9.*
- php: 7.3
laravel: 9.*
- php: 8.3
laravel: 8.*
- php: 8.2
laravel: 8.*
name: P${{ matrix.php }} - Laravel${{ matrix.laravel }} name: P${{ matrix.php }} - Laravel${{ matrix.laravel }}
steps: steps:
- name: Checkout code - name: Checkout code
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
with: with:
php-version: 8.0 php-version: 8.1
coverage: none coverage: none
extensions: pdo_sqlite extensions: pdo_sqlite
+2 -14
View File
@@ -20,26 +20,14 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-20.04, windows-2019] os: [ubuntu-20.04, windows-2019]
php: [8.3, 8.2, 8.1, 8.0, 7.4, 7.3] php: [8.3, 8.2, 8.1, 8.0]
laravel: [8.*, 9.*, 10.*] laravel: [9.*, 10.*]
dependency-version: [prefer-lowest, prefer-stable] dependency-version: [prefer-lowest, prefer-stable]
exclude: exclude:
- php: 8.0 - php: 8.0
laravel: 10.* laravel: 10.*
- php: 7.4
laravel: 10.*
- php: 7.3
laravel: 10.*
- php: 8.0 - php: 8.0
dependency-version: prefer-lowest dependency-version: prefer-lowest
- php: 7.4
laravel: 9.*
- php: 7.3
laravel: 9.*
- os: windows-2019
php: 7.4
- os: windows-2019
php: 7.3
steps: steps:
- name: Set git to use LF - name: Set git to use LF
if: ${{ matrix.os == 'windows-2019' }} if: ${{ matrix.os == 'windows-2019' }}
+3 -3
View File
@@ -1,8 +1,8 @@
.phpunit.result.cache .phpunit.result.cache
/.idea /.idea
/.php_cs /.php-cs-fixer.cache
/.php_cs.cache /.php-cs-fixer.php
/.php_cs.tests.cache /.php-cs-fixer.tests.cache
/composer.lock /composer.lock
/vendor /vendor
@@ -30,7 +30,6 @@ return [
], ],
'return_type_declaration' => true, 'return_type_declaration' => true,
'short_scalar_cast' => true, 'short_scalar_cast' => true,
'single_blank_line_before_namespace' => true,
'single_trait_insert_per_statement' => true, 'single_trait_insert_per_statement' => true,
'ternary_operator_spaces' => true, 'ternary_operator_spaces' => true,
'visibility_required' => [ 'visibility_required' => [
@@ -56,7 +55,7 @@ return [
'no_unused_imports' => true, 'no_unused_imports' => true,
'single_quote' => true, 'single_quote' => true,
'space_after_semicolon' => true, 'space_after_semicolon' => true,
'trailing_comma_in_multiline_array' => true, 'trailing_comma_in_multiline' => true,
'trim_array_spaces' => true, 'trim_array_spaces' => true,
'unary_operator_spaces' => true, 'unary_operator_spaces' => true,
'whitespace_after_comma_in_array' => true, 'whitespace_after_comma_in_array' => true,
+3 -3
View File
@@ -5,10 +5,10 @@ $finder = PhpCsFixer\Finder::create()
->in(__DIR__) ->in(__DIR__)
->exclude('tests'); ->exclude('tests');
$config = require __DIR__ . '/.php_cs.common.php'; $config = require __DIR__ . '/.php-cs-fixer.common.php';
return PhpCsFixer\Config::create() return (new PhpCsFixer\Config())
->setFinder($finder) ->setFinder($finder)
->setRules($config) ->setRules($config)
->setRiskyAllowed(true) ->setRiskyAllowed(true)
->setCacheFile(__DIR__ . '/.php_cs.cache'); ->setCacheFile(__DIR__ . '/.php-cs-fixer.cache');
@@ -5,7 +5,7 @@ $finder = PhpCsFixer\Finder::create()
->in(__DIR__ . '/tests') ->in(__DIR__ . '/tests')
->exclude('__snapshots__'); ->exclude('__snapshots__');
$config = require __DIR__ . '/.php_cs.common.php'; $config = require __DIR__ . '/.php-cs-fixer.common.php';
// Additional rules for tests // Additional rules for tests
$config = array_merge( $config = array_merge(
@@ -15,8 +15,8 @@ $config = array_merge(
] ]
); );
return PhpCsFixer\Config::create() return (new PhpCsFixer\Config())
->setFinder($finder) ->setFinder($finder)
->setRules($config) ->setRules($config)
->setRiskyAllowed(true) ->setRiskyAllowed(true)
->setCacheFile(__DIR__ . '/.php_cs.tests.cache'); ->setCacheFile(__DIR__ . '/.php-cs-fixer.tests.cache');
+43 -2
View File
@@ -2,9 +2,50 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
[Next release](https://github.com/barryvdh/laravel-ide-helper/compare/v2.14.0...master) [Next release](https://github.com/barryvdh/laravel-ide-helper/compare/v2.15.0...master)
-------------- --------------
### Fixed
### Changed
### Added
2024-02-15, 2.15.1
------------------
### Fixed
- Fix final class keyword in wrong position [#1517 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1517)
### Changed
### Added
2024-02-14, 2.15.0
------------------
### Fixed
- Fix case issue in `ModelsCommand::unsetMethod()` [#1453 / leo108](https://github.com/barryvdh/laravel-ide-helper/pull/1453)
- Fix non-facade classes will result in no autocomplete [#841 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/841)
- Skip swoole, otherwise fatal error [#1477 / TimoFrenzel](https://github.com/barryvdh/laravel-ide-helper/pull/1477)
- Fix vulnerability CVE-2021-43608 [#1392 / allanlaal](https://github.com/barryvdh/laravel-ide-helper/pull/1392)
- Reset foreignKeyConstraintsColumns on model loop start [#1461 / snmatsui](https://github.com/barryvdh/laravel-ide-helper/pull/1461)
- Accept scope & scopes as relation [#1452 / Muetze42](https://github.com/barryvdh/laravel-ide-helper/pull/1452)
- Fix #1300 relation_return_type must take precedence if it is defined [#1394 / menthol](https://github.com/barryvdh/laravel-ide-helper/pull/1394)
### Changed
- Disable inspections of helper files [#1486 / eidng8](https://github.com/barryvdh/laravel-ide-helper/pull/1486)
- Removed support for Laravel 8 and therefore for PHP < 8.0 [#1504 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1504)
### Added
- Add support for enum default arguments using enum cases. [#1464 / d8vjork](https://github.com/barryvdh/laravel-ide-helper/pull/1464)
- Add support for real-time facades in the helper file. [#1455 / filipac](https://github.com/barryvdh/laravel-ide-helper/pull/1455)
- Add support for relations with composite keys. [#1479 / calebdw](https://github.com/barryvdh/laravel-ide-helper/pull/1479)
- Add support for attribute accessors with no backing field or type hinting [#1411 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1411).
- Add support for AsCollection and AsArrayObject casts [#1393 / pataar](https://github.com/barryvdh/laravel-ide-helper/pull/1393)
- Reintroduce support for multi-db setups [#1426 / benpoulson](https://github.com/barryvdh/laravel-ide-helper/pull/1426)
- Support the BINARY(...) database field type [#1434 / Sfonxs](https://github.com/barryvdh/laravel-ide-helper/pull/1434)
- Add AllowDynamicProperties Attribute to cooperate with php8.2 deprecation [#1428 / GeoSot](https://github.com/barryvdh/laravel-ide-helper/pull/1428)
2024-02-05, 2.14.0 2024-02-05, 2.14.0
------------------ ------------------
@@ -13,12 +54,12 @@ All notable changes to this project will be documented in this file.
- Refactor resolving of null information for custom casted attribute types [#1330 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1330) - Refactor resolving of null information for custom casted attribute types [#1330 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1330)
### Fixed ### Fixed
- Add support for attribute accessors marked as protected. [#1339 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1339)
- Catch exceptions when loading aliases [#1465 / dongm2ez](https://github.com/barryvdh/laravel-ide-helper/pull/1465) - Catch exceptions when loading aliases [#1465 / dongm2ez](https://github.com/barryvdh/laravel-ide-helper/pull/1465)
### Added ### Added
- Add support for nikic/php-parser 5 (next to 4) [#1502 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1502) - Add support for nikic/php-parser 5 (next to 4) [#1502 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1502)
- Add support for `immutable_date:*` and `immutable_datetime:*` casts. [#1380 / thekonz](https://github.com/barryvdh/laravel-ide-helper/pull/1380) - Add support for `immutable_date:*` and `immutable_datetime:*` casts. [#1380 / thekonz](https://github.com/barryvdh/laravel-ide-helper/pull/1380)
- Add support for attribute accessors marked as protected. [#1339 / pindab0ter](https://github.com/barryvdh/laravel-ide-helper/pull/1339)
2023-02-04, 2.13.0 2023-02-04, 2.13.0
------------------ ------------------
+9 -5
View File
@@ -11,7 +11,7 @@
This package generates helper files that enable your IDE to provide accurate autocompletion. This package generates helper files that enable your IDE to provide accurate autocompletion.
Generation is done based on the files in your project, so they are always up-to-date. Generation is done based on the files in your project, so they are always up-to-date.
It supports Laravel 8+ and PHP 7.3+ It supports Laravel 9+ and PHP 8.0+
- [Installation](#installation) - [Installation](#installation)
- [Usage](#usage) - [Usage](#usage)
@@ -72,7 +72,7 @@ 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!_ _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: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: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:meta` - [PhpStorm Meta file](#phpstorm-meta-for-container-instances)
@@ -113,6 +113,10 @@ The generator tries to identify the real class, but if it cannot be found, you c
Some classes need a working database connection. If you do not have a default working connection, some facades will not be included. Some classes need a working database connection. If you do not have a default working connection, some facades will not be included.
You can use an in-memory SQLite driver by adding the `-M` option. You can use an in-memory SQLite driver by adding the `-M` option.
If you use [real-time facades](https://laravel.com/docs/master/facades#real-time-facades) in your app, those will also be included in the generated file using a `@mixin` annotation and extending the original class underneath the facade.
**Note**: this feature uses the generated real-time facades files in the `storage/framework/cache` folder. Those files are generated on-demand as you use the real-time facade, so if the framework has not generated that first, it will not be included in the helper file. Run the route/command/code first and then regenerate the helper file and this time the real-time facade will be included in it.
You can choose to include helper files. This is not enabled by default, but you can override it with the `--helpers (-H)` option. You can choose to include helper files. This is not enabled by default, but you can override it with the `--helpers (-H)` option.
The `Illuminate/Support/helpers.php` is already set up, but you can add/remove your own files in the config file. The `Illuminate/Support/helpers.php` is already set up, but you can add/remove your own files in the config file.
@@ -145,9 +149,9 @@ The class name will be different from the model, avoiding the IDE duplicate anno
> Please make sure to back up your models, before writing the info. > Please make sure to back up your models, before writing the info.
Writing to the models should keep the existing comments and only append new properties/methods. Writing to the models should keep the existing comments and only append new properties/methods. It will not update changed properties/methods.
The existing PHPDoc is replaced, or added if not found.
With the `--reset (-R)` option, the existing PHPDocs are ignored, and only the newly found columns/relations are saved as PHPDocs. With the `--reset (-R)` option, the whole existing PHPDoc is replaced, including any comments that have been made. The `--smart-reset` option will instead keep the 'text' part of the phpdoc comment, and just replace all the property/method defininitions.
```bash ```bash
php artisan ide-helper:models "App\Models\Post" php artisan ide-helper:models "App\Models\Post"
+13 -13
View File
@@ -20,26 +20,26 @@
} }
], ],
"require": { "require": {
"php": "^7.3 || ^8.0", "php": "^8.0",
"ext-json": "*", "ext-json": "*",
"barryvdh/reflection-docblock": "^2.0.6", "barryvdh/reflection-docblock": "^2.0.6",
"composer/class-map-generator": "^1.0", "composer/class-map-generator": "^1.0",
"doctrine/dbal": "^2.6 || ^3", "doctrine/dbal": "^2.6 || ^3.1.4",
"illuminate/console": "^8 || ^9 || ^10", "illuminate/console": "^9 || ^10",
"illuminate/filesystem": "^8 || ^9 || ^10", "illuminate/filesystem": "^9 || ^10",
"illuminate/support": "^8 || ^9 || ^10", "illuminate/support": "^9 || ^10",
"nikic/php-parser": "^4.18 || ^5", "nikic/php-parser": "^4.18 || ^5",
"phpdocumentor/type-resolver": "^1.1.0" "phpdocumentor/type-resolver": "^1.1.0"
}, },
"require-dev": { "require-dev": {
"ext-pdo_sqlite": "*", "ext-pdo_sqlite": "*",
"friendsofphp/php-cs-fixer": "^2", "friendsofphp/php-cs-fixer": "^3",
"illuminate/config": "^8 || ^9 || ^10", "illuminate/config": "^9 || ^10",
"illuminate/view": "^8 || ^9 || ^10", "illuminate/view": "^9 || ^10",
"mockery/mockery": "^1.4", "mockery/mockery": "^1.4",
"orchestra/testbench": "^6 || ^7 || ^8", "orchestra/testbench": "^7 || ^8",
"phpunit/phpunit": "^8.5 || ^9", "phpunit/phpunit": "^9",
"spatie/phpunit-snapshot-assertions": "^3 || ^4", "spatie/phpunit-snapshot-assertions": "^4",
"vimeo/psalm": "^5.4" "vimeo/psalm": "^5.4"
}, },
"suggest": { "suggest": {
@@ -65,7 +65,7 @@
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.14-dev" "dev-master": "2.15-dev"
}, },
"laravel": { "laravel": {
"providers": [ "providers": [
@@ -81,7 +81,7 @@
], ],
"fix-style": [ "fix-style": [
"php-cs-fixer fix", "php-cs-fixer fix",
"php-cs-fixer fix --config=.php_cs.tests.php" "php-cs-fixer fix --config=.php-cs-fixer.tests.php"
], ],
"psalm-set-baseline": "psalm --set-baseline=psalm-baseline.xml", "psalm-set-baseline": "psalm --set-baseline=psalm-baseline.xml",
"test": "phpunit", "test": "phpunit",
+2 -1
View File
@@ -322,7 +322,8 @@ return [
| |
| When using custom relation types its possible for the class name to not contain | When using custom relation types its possible for the class name to not contain
| the proper return type of the relation. The key of the array is the relationship | the proper return type of the relation. The key of the array is the relationship
| method name. The value of the array is the return type of the relation. | method name. The value of the array is the return type of the relation ('many'
| or 'morphTo').
| e.g. `'relationName' => 'many'`. | e.g. `'relationName' => 'many'`.
| |
*/ */
+5
View File
@@ -16,4 +16,9 @@
<code>\Storage</code> <code>\Storage</code>
</UndefinedClass> </UndefinedClass>
</file> </file>
<file src="src/Console/ModelsCommand.php">
<UndefinedClass occurrences="1">
<code>\UnitEnum</code>
</UndefinedClass>
</file>
</files> </files>
+11 -3
View File
@@ -8,6 +8,7 @@
*/ */
?> ?>
/* @noinspection ALL */
// @formatter:off // @formatter:off
// phpcs:ignoreFile // phpcs:ignoreFile
@@ -22,9 +23,6 @@
*/ */
<?php foreach ($namespaces_by_extends_ns as $namespace => $aliases) : ?> <?php foreach ($namespaces_by_extends_ns as $namespace => $aliases) : ?>
<?php if ($namespace == '\Illuminate\Database\Eloquent') :
continue;
endif; ?>
namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> { namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
<?php foreach ($aliases as $alias) : ?> <?php foreach ($aliases as $alias) : ?>
<?= trim($alias->getDocComment(' ')) ?> <?= trim($alias->getDocComment(' ')) ?>
@@ -71,6 +69,16 @@ namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
<?php endforeach; ?> <?php endforeach; ?>
<?php foreach($real_time_facades as $name): ?>
<?php $nested = explode('\\', str_replace('\\' . class_basename($name), '', $name)); ?>
namespace <?php echo implode('\\', $nested); ?> {
/**
* @mixin <?= str_replace('Facades', '', $name) ?>
*/
class <?= class_basename($name) ?> extends <?= str_replace('Facades', '', $name) ?> {}
}
<?php endforeach; ?>
<?php if ($helpers) : ?> <?php if ($helpers) : ?>
namespace { namespace {
<?= $helpers ?> <?= $helpers ?>
+2
View File
@@ -1,6 +1,8 @@
<?= '<?php' ?> <?= '<?php' ?>
/* @noinspection ALL */
// @formatter:off // @formatter:off
// phpcs:ignoreFile
namespace PHPSTORM_META { namespace PHPSTORM_META {
+2 -2
View File
@@ -329,7 +329,7 @@ class Alias
continue; continue;
} }
$method = new \ReflectionMethod($className, $name); $method = new \ReflectionMethod($className, $name);
$class = new \ReflectionClass($className); $class = new ReflectionClass($className);
if (!in_array($magic, $this->usedMethods)) { if (!in_array($magic, $this->usedMethods)) {
if ($class !== $this->root) { if ($class !== $this->root) {
@@ -348,7 +348,7 @@ class Alias
protected function detectMethods() protected function detectMethods()
{ {
foreach ($this->classes as $class) { foreach ($this->classes as $class) {
$reflection = new \ReflectionClass($class); $reflection = new ReflectionClass($class);
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC); $methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
if ($methods) { if ($methods) {
+2 -2
View File
@@ -42,7 +42,7 @@ class GeneratorCommand extends Command
/** @var \Illuminate\Config\Repository */ /** @var \Illuminate\Config\Repository */
protected $config; protected $config;
/** @var \Illuminate\Filesystem\Filesystem */ /** @var Filesystem */
protected $files; protected $files;
/** @var \Illuminate\View\Factory */ /** @var \Illuminate\View\Factory */
@@ -54,7 +54,7 @@ class GeneratorCommand extends Command
/** /**
* *
* @param \Illuminate\Config\Repository $config * @param \Illuminate\Config\Repository $config
* @param \Illuminate\Filesystem\Filesystem $files * @param Filesystem $files
* @param \Illuminate\View\Factory $view * @param \Illuminate\View\Factory $view
*/ */
public function __construct( public function __construct(
+92 -53
View File
@@ -23,6 +23,8 @@ use Illuminate\Console\Command;
use Illuminate\Contracts\Database\Eloquent\Castable; use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes; use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@@ -38,6 +40,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Filesystem\Filesystem; use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use phpDocumentor\Reflection\Types\ContextFactory; use phpDocumentor\Reflection\Types\ContextFactory;
@@ -266,6 +269,7 @@ class ModelsCommand extends Command
} }
$this->properties = []; $this->properties = [];
$this->methods = []; $this->methods = [];
$this->foreignKeyConstraintsColumns = [];
if (class_exists($name)) { if (class_exists($name)) {
try { try {
// handle abstract classes, interfaces, ... // handle abstract classes, interfaces, ...
@@ -354,7 +358,7 @@ class ModelsCommand extends Command
/** /**
* cast the properties's type from $casts. * cast the properties's type from $casts.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
*/ */
public function castPropertiesType($model) public function castPropertiesType($model)
{ {
@@ -417,9 +421,13 @@ class ModelsCommand extends Command
case 'immutable_datetime': case 'immutable_datetime':
$realType = '\Carbon\CarbonImmutable'; $realType = '\Carbon\CarbonImmutable';
break; break;
case AsCollection::class:
case 'collection': case 'collection':
$realType = '\Illuminate\Support\Collection'; $realType = '\Illuminate\Support\Collection';
break; break;
case AsArrayObject::class:
$realType = '\ArrayObject';
break;
default: default:
// In case of an optional custom cast parameter , only evaluate // In case of an optional custom cast parameter , only evaluate
// the `$type` until the `:` // the `$type` until the `:`
@@ -498,7 +506,7 @@ class ModelsCommand extends Command
/** /**
* Load the properties from the database table. * Load the properties from the database table.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
* *
* @throws DBALException If custom field failed to register * @throws DBALException If custom field failed to register
*/ */
@@ -510,6 +518,10 @@ class ModelsCommand extends Command
$databasePlatform = $schema->getDatabasePlatform(); $databasePlatform = $schema->getDatabasePlatform();
$databasePlatform->registerDoctrineTypeMapping('enum', 'string'); $databasePlatform->registerDoctrineTypeMapping('enum', 'string');
if (strpos($table, '.')) {
[$database, $table] = explode('.', $table);
}
$platformName = $databasePlatform->getName(); $platformName = $databasePlatform->getName();
$customTypes = $this->laravel['config']->get("ide-helper.custom_db_types.{$platformName}", []); $customTypes = $this->laravel['config']->get("ide-helper.custom_db_types.{$platformName}", []);
foreach ($customTypes as $yourTypeName => $doctrineTypeName) { foreach ($customTypes as $yourTypeName => $doctrineTypeName) {
@@ -546,6 +558,7 @@ class ModelsCommand extends Command
case 'datetimetz': case 'datetimetz':
case 'datetime': case 'datetime':
case 'decimal': case 'decimal':
case 'binary':
$type = 'string'; $type = 'string';
break; break;
case 'integer': case 'integer':
@@ -602,7 +615,7 @@ class ModelsCommand extends Command
} }
/** /**
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
*/ */
public function getPropertiesFromMethods($model) public function getPropertiesFromMethods($model)
{ {
@@ -613,10 +626,7 @@ class ModelsCommand extends Command
// methods that resemble mutators but aren't. // methods that resemble mutators but aren't.
$reflections = array_filter($reflections, function (\ReflectionMethod $methodReflection) { $reflections = array_filter($reflections, function (\ReflectionMethod $methodReflection) {
return !$methodReflection->isPrivate() && !( return !$methodReflection->isPrivate() && !(
in_array( $methodReflection->getDeclaringClass()->getName() === Model::class && (
\Illuminate\Database\Eloquent\Concerns\HasAttributes::class,
$methodReflection->getDeclaringClass()->getTraitNames()
) && (
$methodReflection->getName() === 'setClassCastableAttribute' || $methodReflection->getName() === 'setClassCastableAttribute' ||
$methodReflection->getName() === 'setEnumCastableAttribute' $methodReflection->getName() === 'setEnumCastableAttribute'
) )
@@ -642,18 +652,15 @@ class ModelsCommand extends Command
$this->setProperty($name, $type, true, null, $comment); $this->setProperty($name, $type, true, null, $comment);
} }
} elseif ($isAttribute) { } elseif ($isAttribute) {
$name = Str::snake($method); $types = $this->getAttributeTypes($model, $reflection);
$types = $this->getAttributeReturnType($model, $reflection); $type = $this->getTypeInModel($model, $types->get('get') ?: $types->get('set')) ?: null;
$comment = $this->getCommentFromDocBlock($reflection); $this->setProperty(
Str::snake($method),
if ($types->has('get')) { $type,
$type = $this->getTypeInModel($model, $types['get']); $types->has('get'),
$this->setProperty($name, $type, true, null, $comment); $types->has('set'),
} $this->getCommentFromDocBlock($reflection)
);
if ($types->has('set')) {
$this->setProperty($name, null, null, true, $comment);
}
} elseif ( } elseif (
Str::startsWith($method, 'set') && Str::endsWith( Str::startsWith($method, 'set') && Str::endsWith(
$method, $method,
@@ -666,7 +673,7 @@ class ModelsCommand extends Command
$comment = $this->getCommentFromDocBlock($reflection); $comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, null, null, true, $comment); $this->setProperty($name, null, null, true, $comment);
} }
} elseif (Str::startsWith($method, 'scope') && $method !== 'scopeQuery') { } elseif (Str::startsWith($method, 'scope') && $method !== 'scopeQuery' && $method !== 'scope' && $method !== 'scopes') {
//Magic set<name>Attribute //Magic set<name>Attribute
$name = Str::camel(substr($method, 5)); $name = Str::camel(substr($method, 5));
if (!empty($name)) { if (!empty($name)) {
@@ -752,9 +759,14 @@ class ModelsCommand extends Command
get_class($relationObj->getRelated()) get_class($relationObj->getRelated())
); );
$relationReturnType = $this->getRelationReturnTypes()[$relation] ?? false;
if ( if (
strpos(get_class($relationObj), 'Many') !== false || $relationReturnType === 'many' ||
($this->getRelationReturnTypes()[$relation] ?? '') === 'many' (
!$relationReturnType &&
strpos(get_class($relationObj), 'Many') !== false
)
) { ) {
//Collection or array of models (because Collection is Arrayable) //Collection or array of models (because Collection is Arrayable)
$relatedClass = '\\' . get_class($relationObj->getRelated()); $relatedClass = '\\' . get_class($relationObj->getRelated());
@@ -777,12 +789,15 @@ class ModelsCommand extends Command
'int|null', 'int|null',
true, true,
false false
// What kind of comments should be added to the relation count here? // What kind of comments should be added to the relation count here?
); );
} }
} elseif ( } elseif (
$relation === 'morphTo' || $relationReturnType === 'morphTo' ||
($this->getRelationReturnTypes()[$relation] ?? '') === 'morphTo' (
!$relationReturnType &&
$relation === 'morphTo'
)
) { ) {
// Model isn't specified because relation is polymorphic // Model isn't specified because relation is polymorphic
$this->setProperty( $this->setProperty(
@@ -837,12 +852,17 @@ class ModelsCommand extends Command
$fkProp = $reflectionObj->getProperty('foreignKey'); $fkProp = $reflectionObj->getProperty('foreignKey');
$fkProp->setAccessible(true); $fkProp->setAccessible(true);
if ($relation === 'belongsTo') { foreach (Arr::wrap($fkProp->getValue($relationObj)) as $foreignKey) {
return isset($this->nullableColumns[$fkProp->getValue($relationObj)]) || if (isset($this->nullableColumns[$foreignKey])) {
!in_array($fkProp->getValue($relationObj), $this->foreignKeyConstraintsColumns, true); return true;
}
if (!in_array($foreignKey, $this->foreignKeyConstraintsColumns, true)) {
return true;
}
} }
return isset($this->nullableColumns[$fkProp->getValue($relationObj)]); return false;
} }
/** /**
@@ -891,7 +911,12 @@ class ModelsCommand extends Command
public function unsetMethod($name) public function unsetMethod($name)
{ {
unset($this->methods[strtolower($name)]); foreach ($this->methods as $k => $v) {
if (strtolower($k) === strtolower($name)) {
unset($this->methods[$k]);
return;
}
}
} }
public function getMethodType(Model $model, string $classType) public function getMethodType(Model $model, string $classType)
@@ -1056,7 +1081,9 @@ class ModelsCommand extends Command
} }
$classname = $this->write_mixin ? $mixinClassName : $classname; $classname = $this->write_mixin ? $mixinClassName : $classname;
$output = "namespace {$namespace}{\n{$docComment}\n\t{$keyword}class {$classname} ";
$allowDynamicAttributes = $this->write_mixin ? "#[\AllowDynamicProperties]\n\t" : '';
$output = "namespace {$namespace}{\n{$docComment}\n\t{$allowDynamicAttributes}{$keyword}class {$classname} ";
if (!$this->write_mixin) { if (!$this->write_mixin) {
$output .= "extends \Eloquent "; $output .= "extends \Eloquent ";
@@ -1099,6 +1126,8 @@ class ModelsCommand extends Command
$default = 'null'; $default = 'null';
} elseif (is_int($default)) { } elseif (is_int($default)) {
//$default = $default; //$default = $default;
} elseif ($default instanceof \UnitEnum) {
$default = '\\' . get_class($default) . '::' . $default->name;
} else { } else {
$default = "'" . trim($default) . "'"; $default = "'" . trim($default) . "'";
} }
@@ -1126,7 +1155,7 @@ class ModelsCommand extends Command
return '\Illuminate\Database\Eloquent\Collection'; return '\Illuminate\Database\Eloquent\Collection';
} }
/** @var \Illuminate\Database\Eloquent\Model $model */ /** @var Model $model */
$model = new $className(); $model = new $className();
return '\\' . get_class($model->newCollection()); return '\\' . get_class($model->newCollection());
} }
@@ -1173,7 +1202,10 @@ class ModelsCommand extends Command
return $this->laravel['config']->get('ide-helper.model_camel_case_properties', false); return $this->laravel['config']->get('ide-helper.model_camel_case_properties', false);
} }
protected function getAttributeReturnType(Model $model, \ReflectionMethod $reflectionMethod): Collection /**
* @psalm-suppress NoValue
*/
protected function getAttributeTypes(Model $model, \ReflectionMethod $reflectionMethod): Collection
{ {
// Private/protected ReflectionMethods require setAccessible prior to PHP 8.1 // Private/protected ReflectionMethods require setAccessible prior to PHP 8.1
$reflectionMethod->setAccessible(true); $reflectionMethod->setAccessible(true);
@@ -1181,13 +1213,25 @@ class ModelsCommand extends Command
/** @var Attribute $attribute */ /** @var Attribute $attribute */
$attribute = $reflectionMethod->invoke($model); $attribute = $reflectionMethod->invoke($model);
return collect([ $methods = new Collection();
'get' => $attribute->get ? optional(new \ReflectionFunction($attribute->get))->getReturnType() : null,
'set' => $attribute->set ? optional(new \ReflectionFunction($attribute->set))->getReturnType() : null, if ($attribute->get) {
]) $methods['get'] = optional(new \ReflectionFunction($attribute->get))->getReturnType();
->filter() }
if ($attribute->set) {
$function = optional(new \ReflectionFunction($attribute->set));
if ($function->getNumberOfParameters() === 0) {
$methods['set'] = null;
} else {
$methods['set'] = $function->getParameters()[0]->getType();
}
}
return $methods
->map(function ($type) { ->map(function ($type) {
if ($type instanceof \ReflectionUnionType) { if ($type === null) {
$types = collect([]);
} elseif ($type instanceof \ReflectionUnionType) {
$types = collect($type->getTypes()) $types = collect($type->getTypes())
/** @var ReflectionType $reflectionType */ /** @var ReflectionType $reflectionType */
->map(function ($reflectionType) { ->map(function ($reflectionType) {
@@ -1198,7 +1242,7 @@ class ModelsCommand extends Command
$types = collect($this->extractReflectionTypes($type)); $types = collect($this->extractReflectionTypes($type));
} }
if ($type->allowsNull()) { if ($type && $type->allowsNull()) {
$types->push('null'); $types->push('null');
} }
@@ -1285,7 +1329,7 @@ class ModelsCommand extends Command
/** /**
* Generates methods provided by the SoftDeletes trait * Generates methods provided by the SoftDeletes trait
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
*/ */
protected function getSoftDeleteMethods($model) protected function getSoftDeleteMethods($model)
{ {
@@ -1302,7 +1346,7 @@ class ModelsCommand extends Command
/** /**
* Generate factory method from "HasFactory" trait. * Generate factory method from "HasFactory" trait.
* *
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
*/ */
protected function getFactoryMethods($model) protected function getFactoryMethods($model)
{ {
@@ -1330,16 +1374,12 @@ class ModelsCommand extends Command
return; return;
} }
if (version_compare($this->laravel->version(), '9', '>=')) { $this->setMethod('factory', $factory, ['$count = null, $state = []']);
$this->setMethod('factory', $factory, ['$count = null, $state = []']);
} else {
$this->setMethod('factory', $factory, ['...$parameters']);
}
} }
/** /**
* Generates methods that return collections * Generates methods that return collections
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
*/ */
protected function getCollectionMethods($model) protected function getCollectionMethods($model)
{ {
@@ -1382,7 +1422,7 @@ class ModelsCommand extends Command
return $type; return $type;
} }
$reflection = new \ReflectionClass($type); $reflection = new ReflectionClass($type);
if (!$reflection->implementsInterface(Castable::class)) { if (!$reflection->implementsInterface(Castable::class)) {
return $type; return $type;
@@ -1414,7 +1454,7 @@ class ModelsCommand extends Command
return $type; return $type;
} }
$reflection = new \ReflectionClass($type); $reflection = new ReflectionClass($type);
if (!$reflection->implementsInterface(CastsAttributes::class)) { if (!$reflection->implementsInterface(CastsAttributes::class)) {
return $type; return $type;
@@ -1452,8 +1492,7 @@ class ModelsCommand extends Command
{ {
$reflection = $model instanceof ReflectionClass $reflection = $model instanceof ReflectionClass
? $model ? $model
: new ReflectionObject($model) : new ReflectionObject($model);
;
$className = trim($className, '\\'); $className = trim($className, '\\');
$writingToExternalFile = !$this->write || $this->write_mixin; $writingToExternalFile = !$this->write || $this->write_mixin;
@@ -1620,7 +1659,7 @@ class ModelsCommand extends Command
} }
/** /**
* @param \Illuminate\Database\Eloquent\Model $model * @param Model $model
* @throws \Illuminate\Contracts\Container\BindingResolutionException * @throws \Illuminate\Contracts\Container\BindingResolutionException
* @throws \RuntimeException * @throws \RuntimeException
*/ */
+61 -3
View File
@@ -13,8 +13,13 @@ namespace Barryvdh\LaravelIdeHelper;
use Illuminate\Foundation\AliasLoader; use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use Illuminate\Support\Traits\Macroable; use Illuminate\Support\Traits\Macroable;
use PhpParser\Lexer\Emulative;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Parser\Php7;
use ReflectionClass; use ReflectionClass;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
@@ -26,7 +31,7 @@ class Generator
/** @var \Illuminate\View\Factory */ /** @var \Illuminate\View\Factory */
protected $view; protected $view;
/** @var \Symfony\Component\Console\Output\OutputInterface */ /** @var OutputInterface */
protected $output; protected $output;
protected $extra = []; protected $extra = [];
@@ -37,7 +42,7 @@ class Generator
/** /**
* @param \Illuminate\Config\Repository $config * @param \Illuminate\Config\Repository $config
* @param \Illuminate\View\Factory $view * @param \Illuminate\View\Factory $view
* @param \Symfony\Component\Console\Output\OutputInterface $output * @param OutputInterface $output
* @param string $helpers * @param string $helpers
*/ */
public function __construct( public function __construct(
@@ -75,6 +80,7 @@ class Generator
return $this->view->make('helper') return $this->view->make('helper')
->with('namespaces_by_extends_ns', $this->getAliasesByExtendsNamespace()) ->with('namespaces_by_extends_ns', $this->getAliasesByExtendsNamespace())
->with('namespaces_by_alias_ns', $this->getAliasesByAliasNamespace()) ->with('namespaces_by_alias_ns', $this->getAliasesByAliasNamespace())
->with('real_time_facades', $this->getRealTimeFacades())
->with('helpers', $this->helpers) ->with('helpers', $this->helpers)
->with('version', $app->version()) ->with('version', $app->version())
->with('include_fluent', $this->config->get('ide-helper.include_fluent', true)) ->with('include_fluent', $this->config->get('ide-helper.include_fluent', true))
@@ -153,6 +159,11 @@ class Generator
continue; continue;
} }
// Skip the swoole
if ($facade == 'SwooleTW\Http\Server\Facades\Server' && $name == 'Server' && !class_exists('Swoole\Http\Server')) {
continue;
}
$magicMethods = array_key_exists($name, $this->magic) ? $this->magic[$name] : []; $magicMethods = array_key_exists($name, $this->magic) ? $this->magic[$name] : [];
$alias = new Alias($this->config, $name, $facade, $magicMethods, $this->interfaces); $alias = new Alias($this->config, $name, $facade, $magicMethods, $this->interfaces);
if ($alias->isValid()) { if ($alias->isValid()) {
@@ -168,6 +179,51 @@ class Generator
return $aliases; return $aliases;
} }
protected function getRealTimeFacades()
{
$facades = [];
$realTimeFacadeFiles = glob(storage_path('framework/cache/facade-*.php'));
foreach ($realTimeFacadeFiles as $file) {
try {
$name = $this->getFullyQualifiedClassNameInFile($file);
$facades[$name] = $name;
} catch (\Exception $e) {
continue;
}
}
return $facades;
}
protected function getFullyQualifiedClassNameInFile(string $path)
{
$contents = file_get_contents($path);
$parsers = new Php7(new Emulative());
$parsed = collect($parsers->parse($contents) ?: []);
$namespace = $parsed->first(function ($node) {
return $node instanceof Namespace_;
});
if ($namespace) {
$name = $namespace->name->toString();
$class = collect($namespace->stmts)->first(function ($node) {
return $node instanceof Class_;
});
if ($class) {
$name .= '\\' . $class->name->toString();
}
return $name;
}
}
/** /**
* Regroup aliases by namespace of extended classes * Regroup aliases by namespace of extended classes
* *
@@ -175,7 +231,9 @@ class Generator
*/ */
protected function getAliasesByExtendsNamespace() protected function getAliasesByExtendsNamespace()
{ {
$aliases = $this->getValidAliases(); $aliases = $this->getValidAliases()->filter(static function (Alias $alias) {
return is_subclass_of($alias->getExtends(), Facade::class);
});
$this->addMacroableClasses($aliases); $this->addMacroableClasses($aliases);
+4 -4
View File
@@ -16,15 +16,15 @@ class GenerateModelHelper
*/ */
public static $shouldRun = false; public static $shouldRun = false;
/** @var \Illuminate\Contracts\Console\Kernel */ /** @var Artisan */
protected $artisan; protected $artisan;
/** @var \Illuminate\Contracts\Config\Repository */ /** @var Config */
protected $config; protected $config;
/** /**
* @param \Illuminate\Contracts\Console\Kernel $artisan * @param Artisan $artisan
* @param \Illuminate\Contracts\Config\Repository $config * @param Config $config
*/ */
public function __construct(Artisan $artisan, Config $config) public function __construct(Artisan $artisan, Config $config)
{ {
+1 -1
View File
@@ -22,7 +22,7 @@ use Illuminate\Support\Str;
class Method class Method
{ {
/** @var \Barryvdh\Reflection\DocBlock */ /** @var DocBlock */
protected $phpdoc; protected $phpdoc;
/** @var \ReflectionMethod */ /** @var \ReflectionMethod */
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Models;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Model;
class AdvancedCast extends Model
{
protected $casts = [
'cast_to_date_serialization' => 'date:Y-m-d',
'cast_to_datetime_serialization' => 'datetime:Y-m-d H:i:s',
'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',
'cast_to_as_collection' => AsCollection::class,
'cast_to_as_array_object' => AsArrayObject::class,
];
}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
use Illuminate\Foundation\Application;
class Test extends AbstractModelsCommand
{
public function test(): void
{
if (!version_compare(Application::VERSION, '8.28', '>=')) {
$this->markTestSkipped(
'This test only works in Laravel >= 8.28'
);
}
$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\AdvancedCasts\Models;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Models\AdvancedCast
*
* @property \Illuminate\Support\Carbon $cast_to_date_serialization
* @property \Illuminate\Support\Carbon $cast_to_datetime_serialization
* @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
* @property \Illuminate\Support\Collection $cast_to_as_collection
* @property \ArrayObject $cast_to_as_array_object
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast query()
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToAsArrayObject($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToAsCollection($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToCustomDatetime($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToDateSerialization($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToDatetimeSerialization($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToEncrypted($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToEncryptedArray($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToEncryptedCollection($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToEncryptedJson($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToEncryptedObject($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToImmutableCustomDatetime($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToImmutableDate($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToImmutableDatetime($value)
* @method static \Illuminate\Database\Eloquent\Builder|AdvancedCast whereCastToTimestamp($value)
* @mixin \Eloquent
*/
class AdvancedCast extends Model
{
protected $casts = [
'cast_to_date_serialization' => 'date:Y-m-d',
'cast_to_datetime_serialization' => 'datetime:Y-m-d H:i:s',
'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',
'cast_to_as_collection' => AsCollection::class,
'cast_to_as_array_object' => AsArrayObject::class,
];
}
@@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Model;
class Simple extends Model class Simple extends Model
{ {
// With a backed property
protected function name(): Attribute protected function name(): Attribute
{ {
return new Attribute( return new Attribute(
@@ -16,18 +17,91 @@ class Simple extends Model
return $name; return $name;
}, },
function (?string $name): ?string { function (?string $name): ?string {
return $name === null ? null : ucfirst($name); return $name;
} }
); );
} }
// Without backed properties
protected function typeHintedGetAndSet(): Attribute
{
return new Attribute(
function (): ?string {
return $this->name;
},
function (?string $name) {
$this->name = $name;
}
);
}
protected function divergingTypeHintedGetAndSet(): Attribute
{
return new Attribute(
function (): int {
return strlen($this->name);
},
function (?string $name) {
$this->name = $name;
}
);
}
protected function typeHintedGet(): Attribute
{
return Attribute::get(function (): ?string {
return $this->name;
});
}
protected function typeHintedSet(): Attribute
{
return Attribute::set(function (?string $name) {
$this->name = $name;
});
}
protected function nonTypeHintedGetAndSet(): Attribute
{
return new Attribute(
function () {
return $this->name;
},
function ($name) {
$this->name = $name;
}
);
}
protected function nonTypeHintedGet(): Attribute
{
return Attribute::get(function () {
return $this->name;
});
}
protected function nonTypeHintedSet(): Attribute
{
return Attribute::set(function ($name) {
$this->name = $name;
});
}
protected function parameterlessSet(): Attribute
{
return Attribute::set(function () {
$this->name = null;
});
}
/** /**
* ide-helper does not recognize this method being an Attribute * ide-helper does not recognize this method being an Attribute
* because the method has no actual return type; * because the method has no actual return type;
* phpdoc is ignored here deliberately due to performance reasons and also * phpdoc is ignored here deliberately due to performance reasons and also
* isn't supported by Laravel itself. * isn't supported by Laravel itself.
* *
* @return \Illuminate\Database\Eloquent\Casts\Attribute * @return Attribute
*/ */
protected function notAnAttribute() protected function notAnAttribute()
{ {
@@ -9,15 +9,6 @@ use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends 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 public function test(): void
{ {
$command = $this->app->make(ModelsCommand::class); $command = $this->app->make(ModelsCommand::class);
@@ -11,7 +11,15 @@ use Illuminate\Database\Eloquent\Model;
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Attributes\Models\Simple * Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Attributes\Models\Simple
* *
* @property integer $id * @property integer $id
* @property int $diverging_type_hinted_get_and_set
* @property string|null $name * @property string|null $name
* @property-read mixed $non_type_hinted_get
* @property mixed $non_type_hinted_get_and_set
* @property-write mixed $non_type_hinted_set
* @property-write mixed $parameterless_set
* @property-read string|null $type_hinted_get
* @property string|null $type_hinted_get_and_set
* @property-write string|null $type_hinted_set
* @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery() * @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple query() * @method static \Illuminate\Database\Eloquent\Builder|Simple query()
@@ -20,6 +28,7 @@ use Illuminate\Database\Eloquent\Model;
*/ */
class Simple extends Model class Simple extends Model
{ {
// With a backed property
protected function name(): Attribute protected function name(): Attribute
{ {
return new Attribute( return new Attribute(
@@ -27,18 +36,91 @@ class Simple extends Model
return $name; return $name;
}, },
function (?string $name): ?string { function (?string $name): ?string {
return $name === null ? null : ucfirst($name); return $name;
} }
); );
} }
// Without backed properties
protected function typeHintedGetAndSet(): Attribute
{
return new Attribute(
function (): ?string {
return $this->name;
},
function (?string $name) {
$this->name = $name;
}
);
}
protected function divergingTypeHintedGetAndSet(): Attribute
{
return new Attribute(
function (): int {
return strlen($this->name);
},
function (?string $name) {
$this->name = $name;
}
);
}
protected function typeHintedGet(): Attribute
{
return Attribute::get(function (): ?string {
return $this->name;
});
}
protected function typeHintedSet(): Attribute
{
return Attribute::set(function (?string $name) {
$this->name = $name;
});
}
protected function nonTypeHintedGetAndSet(): Attribute
{
return new Attribute(
function () {
return $this->name;
},
function ($name) {
$this->name = $name;
}
);
}
protected function nonTypeHintedGet(): Attribute
{
return Attribute::get(function () {
return $this->name;
});
}
protected function nonTypeHintedSet(): Attribute
{
return Attribute::set(function ($name) {
$this->name = $name;
});
}
protected function parameterlessSet(): Attribute
{
return Attribute::set(function () {
$this->name = null;
});
}
/** /**
* ide-helper does not recognize this method being an Attribute * ide-helper does not recognize this method being an Attribute
* because the method has no actual return type; * because the method has no actual return type;
* phpdoc is ignored here deliberately due to performance reasons and also * phpdoc is ignored here deliberately due to performance reasons and also
* isn't supported by Laravel itself. * isn't supported by Laravel itself.
* *
* @return \Illuminate\Database\Eloquent\Casts\Attribute * @return Attribute
*/ */
protected function notAnAttribute() protected function notAnAttribute()
{ {
@@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\CustomPhpdocTags\Models;
use Illuminate\Database\Eloquent\Model;
/**
* The `@SuppressWarnings` tag below contains no space before the `(` but when
* the class phpdoc is written back, one is inserted.
*
* @link https://github.com/barryvdh/laravel-ide-helper/issues/666
*
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
*/
class Simple extends Model
{
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\CustomPhpdocTags;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
/**
* This test makes sure that custom phpdoc tags are not mangled, e.g.
* there are no spaces inserted etc.
*
* @link https://github.com/barryvdh/laravel-ide-helper/issues/666
*/
public function testNoSpaceAfterCustomPhpdocTag(): 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,24 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\CustomPhpdocTags\Models;
use Illuminate\Database\Eloquent\Model;
/**
* The `@SuppressWarnings` tag below contains no space before the `(` but when
* the class phpdoc is written back, one is inserted.
*
* @link https://github.com/barryvdh/laravel-ide-helper/issues/666
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @property integer $id
* @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
{
}
@@ -17,19 +17,11 @@ class Test extends AbstractModelsCommand
'--write' => true, '--write' => true,
]); ]);
if (PHP_VERSION_ID >= 80000) { $errors = [
$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: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: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',
'Error resolving relation model of Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DynamicRelations\Models\Dynamic:dynamicHasOne() : Attempt to read property "created_at" on null', ];
];
} else {
$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->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay()); $this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
+1 -30
View File
@@ -8,41 +8,12 @@ use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
use Closure; use Closure;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Foundation\Application;
use Illuminate\Support\Str; use Illuminate\Support\Str;
class Test extends AbstractModelsCommand class Test extends AbstractModelsCommand
{ {
public function test_8(): void public function testFactory(): void
{ {
if (!version_compare(Application::VERSION, '8.2', '>=') || !version_compare(Application::VERSION, '9', '<')) {
$this->markTestSkipped(
'This test only works in Laravel >= 8.2 and < 9'
);
}
Factory::guessFactoryNamesUsing(static::getFactoryNameResolver());
$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->assertStringNotContainsString('not found', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
public function test_9(): void
{
if (!version_compare(Application::VERSION, '9', '>=')) {
$this->markTestSkipped(
'This test only works in Laravel >= 9'
);
}
Factory::guessFactoryNamesUsing(static::getFactoryNameResolver()); Factory::guessFactoryNamesUsing(static::getFactoryNameResolver());
$command = $this->app->make(ModelsCommand::class); $command = $this->app->make(ModelsCommand::class);
@@ -1,94 +0,0 @@
<?php
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;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models\ModelWithoutFactory
*
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithoutFactory newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithoutFactory newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ModelWithoutFactory query()
* @mixin \Eloquent
*/
class ModelWithoutFactory extends Model
{
use HasFactory;
}
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults\Enums;
enum PostStatus
{
case Published;
case Unpublished;
case Archived;
}
@@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults\Enums\PostStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function scopeHasStatus(Builder $query, ?PostStatus $status = PostStatus::Published)
{
//
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
if (!version_compare(PHP_VERSION, '8.1', '>=')) {
$this->markTestSkipped(
'This test only works in PHP >= 8.1'
);
}
$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,172 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults\Enums\PostStatus;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults\Models\Post
*
* @property integer $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 integer|null $integer_nullable
* @property integer $integer_not_nullable
* @property integer|null $tiny_integer_nullable
* @property integer $tiny_integer_not_nullable
* @property integer|null $small_integer_nullable
* @property integer $small_integer_not_nullable
* @property integer|null $medium_integer_nullable
* @property integer $medium_integer_not_nullable
* @property integer|null $big_integer_nullable
* @property integer $big_integer_not_nullable
* @property integer|null $unsigned_integer_nullable
* @property integer $unsigned_integer_not_nullable
* @property integer|null $unsigned_tiny_integer_nullable
* @property integer $unsigned_tiny_integer_not_nullable
* @property integer|null $unsigned_small_integer_nullable
* @property integer $unsigned_small_integer_not_nullable
* @property integer|null $unsigned_medium_integer_nullable
* @property integer $unsigned_medium_integer_not_nullable
* @property integer|null $unsigned_big_integer_nullable
* @property integer $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 string|null $decimal_nullable
* @property string $decimal_not_nullable
* @property string|null $unsigned_decimal_nullable
* @property string $unsigned_decimal_not_nullable
* @property integer|null $boolean_nullable
* @property integer $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 integer|null $year_nullable
* @property integer $year_not_nullable
* @property mixed|null $binary_nullable
* @property mixed $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 Builder|Post hasStatus(?\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults\Enums\PostStatus $status = \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpDocWithEnumDefaults\Enums\PostStatus::Published)
* @method static Builder|Post newModelQuery()
* @method static Builder|Post newQuery()
* @method static Builder|Post query()
* @method static Builder|Post whereBigIntegerNotNullable($value)
* @method static Builder|Post whereBigIntegerNullable($value)
* @method static Builder|Post whereBinaryNotNullable($value)
* @method static Builder|Post whereBinaryNullable($value)
* @method static Builder|Post whereBooleanNotNullable($value)
* @method static Builder|Post whereBooleanNullable($value)
* @method static Builder|Post whereCharNotNullable($value)
* @method static Builder|Post whereCharNullable($value)
* @method static Builder|Post whereCreatedAt($value)
* @method static Builder|Post whereDateNotNullable($value)
* @method static Builder|Post whereDateNullable($value)
* @method static Builder|Post whereDatetimeNotNullable($value)
* @method static Builder|Post whereDatetimeNullable($value)
* @method static Builder|Post whereDatetimetzNotNullable($value)
* @method static Builder|Post whereDatetimetzNullable($value)
* @method static Builder|Post whereDecimalNotNullable($value)
* @method static Builder|Post whereDecimalNullable($value)
* @method static Builder|Post whereDoubleNotNullable($value)
* @method static Builder|Post whereDoubleNullable($value)
* @method static Builder|Post whereEnumNotNullable($value)
* @method static Builder|Post whereEnumNullable($value)
* @method static Builder|Post whereFloatNotNullable($value)
* @method static Builder|Post whereFloatNullable($value)
* @method static Builder|Post whereId($value)
* @method static Builder|Post whereIntegerNotNullable($value)
* @method static Builder|Post whereIntegerNullable($value)
* @method static Builder|Post whereIpaddressNotNullable($value)
* @method static Builder|Post whereIpaddressNullable($value)
* @method static Builder|Post whereJsonNotNullable($value)
* @method static Builder|Post whereJsonNullable($value)
* @method static Builder|Post whereJsonbNotNullable($value)
* @method static Builder|Post whereJsonbNullable($value)
* @method static Builder|Post whereLongTextNotNullable($value)
* @method static Builder|Post whereLongTextNullable($value)
* @method static Builder|Post whereMacaddressNotNullable($value)
* @method static Builder|Post whereMacaddressNullable($value)
* @method static Builder|Post whereMediumIntegerNotNullable($value)
* @method static Builder|Post whereMediumIntegerNullable($value)
* @method static Builder|Post whereMediumTextNotNullable($value)
* @method static Builder|Post whereMediumTextNullable($value)
* @method static Builder|Post whereSmallIntegerNotNullable($value)
* @method static Builder|Post whereSmallIntegerNullable($value)
* @method static Builder|Post whereStringNotNullable($value)
* @method static Builder|Post whereStringNullable($value)
* @method static Builder|Post whereTextNotNullable($value)
* @method static Builder|Post whereTextNullable($value)
* @method static Builder|Post whereTimeNotNullable($value)
* @method static Builder|Post whereTimeNullable($value)
* @method static Builder|Post whereTimestampNotNullable($value)
* @method static Builder|Post whereTimestampNullable($value)
* @method static Builder|Post whereTimestamptzNotNullable($value)
* @method static Builder|Post whereTimestamptzNullable($value)
* @method static Builder|Post whereTimetzNotNullable($value)
* @method static Builder|Post whereTimetzNullable($value)
* @method static Builder|Post whereTinyIntegerNotNullable($value)
* @method static Builder|Post whereTinyIntegerNullable($value)
* @method static Builder|Post whereUnsignedBigIntegerNotNullable($value)
* @method static Builder|Post whereUnsignedBigIntegerNullable($value)
* @method static Builder|Post whereUnsignedDecimalNotNullable($value)
* @method static Builder|Post whereUnsignedDecimalNullable($value)
* @method static Builder|Post whereUnsignedIntegerNotNullable($value)
* @method static Builder|Post whereUnsignedIntegerNullable($value)
* @method static Builder|Post whereUnsignedMediumIntegerNotNullable($value)
* @method static Builder|Post whereUnsignedMediumIntegerNullable($value)
* @method static Builder|Post whereUnsignedSmallIntegerNotNullable($value)
* @method static Builder|Post whereUnsignedSmallIntegerNullable($value)
* @method static Builder|Post whereUnsignedTinyIntegerNotNullable($value)
* @method static Builder|Post whereUnsignedTinyIntegerNullable($value)
* @method static Builder|Post whereUpdatedAt($value)
* @method static Builder|Post whereUuidNotNullable($value)
* @method static Builder|Post whereUuidNullable($value)
* @method static Builder|Post whereYearNotNullable($value)
* @method static Builder|Post whereYearNullable($value)
* @mixin \Eloquent
*/
class Post extends Model
{
public function scopeHasStatus(Builder $query, ?PostStatus $status = PostStatus::Published)
{
//
}
}
@@ -0,0 +1,16 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property $someProp
* @method someMethod(string $method)
* @mixin IdeHelperPost
*/
final class FinalPost extends Model
{
}
@@ -6,6 +6,22 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWi
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
/**
* @property $someProp
* @method someMethod(string $method)
* @mixin IdeHelperFinalPost
*/
final class FinalPost extends Model
{
}
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models;
use Illuminate\Database\Eloquent\Model;
/** /**
* @property $someProp * @property $someProp
* @method someMethod(string $method) * @method someMethod(string $method)
@@ -27,6 +43,21 @@ class Post extends Model
*/ */
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models{
/**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models\FinalPost
*
* @property $someProp
* @method someMethod(string $method)
* @method static \Illuminate\Database\Eloquent\Builder|FinalPost newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|FinalPost newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|FinalPost query()
* @mixin \Eloquent
*/
#[\AllowDynamicProperties]
final class IdeHelperFinalPost {}
}
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models{ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models{
/** /**
* Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models\Post * Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models\Post
@@ -184,6 +215,7 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWi
* @method static \Illuminate\Database\Eloquent\Builder|Post whereYearNullable($value) * @method static \Illuminate\Database\Eloquent\Builder|Post whereYearNullable($value)
* @mixin \Eloquent * @mixin \Eloquent
*/ */
#[\AllowDynamicProperties]
class IdeHelperPost {} class IdeHelperPost {}
} }
@@ -11,7 +11,7 @@ class CastableReturnsAnonymousCaster implements Castable
{ {
public static function castUsing(array $arguments) public static function castUsing(array $arguments)
{ {
return new class() implements CastsAttributes { return new class () implements CastsAttributes {
/** /**
* @inheritDoc * @inheritDoc
* @return CastedProperty * @return CastedProperty
@@ -11,7 +11,7 @@ class CastableWithoutReturnType implements Castable
{ {
public static function castUsing(array $arguments) public static function castUsing(array $arguments)
{ {
return new class() implements CastsAttributes { return new class () implements CastsAttributes {
public function get($model, string $key, $value, array $attributes) public function get($model, string $key, $value, array $attributes)
{ {
return new CastedProperty(); return new CastedProperty();
@@ -10,7 +10,7 @@ class CustomCasterWithDocblockReturnFqn implements CastsAttributes
{ {
/** /**
* @inheritDoc * @inheritDoc
* @return \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty * @return CastedProperty
*/ */
public function get($model, string $key, $value, array $attributes) public function get($model, string $key, $value, array $attributes)
{ {
@@ -12,6 +12,6 @@ class UnsetMethod implements ModelHookInterface
{ {
public function run(ModelsCommand $command, Model $model): void public function run(ModelsCommand $command, Model $model): void
{ {
$command->unsetMethod('query'); $command->unsetMethod('newmodelquery');
} }
} }
@@ -76,8 +76,8 @@ use Illuminate\Database\Eloquent\Model;
* @property int $id * @property int $id
* @property-read string $custom * @property-read string $custom
* @method static \Illuminate\Database\Eloquent\Builder|Simple custom($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 newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple query()
* @method static \Illuminate\Database\Eloquent\Builder|Simple whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|Simple whereId($value)
* @mixin \Eloquent * @mixin \Eloquent
*/ */
@@ -0,0 +1,41 @@
<?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 CompositeBelongsToVariation extends Model
{
public $table = 'belongs_to_variations';
public function bothNonNullableWithForeignKeyConstraint(): BelongsTo
{
// Note, duplicating the keys here for simplicity.
return $this->belongsTo(
self::class,
['not_null_column_with_foreign_key_constraint', 'not_null_column_with_foreign_key_constraint'],
['not_null_column_with_foreign_key_constraint', 'not_null_column_with_foreign_key_constraint'],
);
}
public function nonNullableMixedWithoutForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(
self::class,
['not_null_column_with_foreign_key_constraint', 'not_null_column_with_no_foreign_key_constraint'],
['not_null_column_with_foreign_key_constraint', 'not_null_column_with_no_foreign_key_constraint'],
);
}
public function nullableMixedWithForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(
self::class,
['nullable_column_with_no_foreign_key_constraint', 'not_null_column_with_foreign_key_constraint'],
['nullable_column_with_no_foreign_key_constraint', 'not_null_column_with_foreign_key_constraint'],
);
}
}
@@ -107,4 +107,9 @@ class Simple extends Model
{ {
return $this->testToAnyMorphedRelation(Simple::class); return $this->testToAnyMorphedRelation(Simple::class);
} }
public function relationSampleToBadlyNamedNotManyRelation()
{
return $this->testToBadlyNamedNotManyRelation(Simple::class);
}
} }
@@ -6,7 +6,9 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand; use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToAnyMorphedRelationType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToAnyRelationType; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToAnyRelationType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToBadlyNamedNotManyRelationType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToManyRelationType; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToManyRelationType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToOneRelationType; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToOneRelationType;
use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\Config;
@@ -22,11 +24,13 @@ class Test extends AbstractModelsCommand
'testToManyRelation' => SampleToManyRelationType::class, 'testToManyRelation' => SampleToManyRelationType::class,
'testToAnyRelation' => SampleToAnyRelationType::class, 'testToAnyRelation' => SampleToAnyRelationType::class,
'testToAnyMorphedRelation' => SampleToAnyMorphedRelationType::class, 'testToAnyMorphedRelation' => SampleToAnyMorphedRelationType::class,
'testToBadlyNamedNotManyRelation' => SampleToBadlyNamedNotManyRelationType::class,
]); ]);
Config::set('ide-helper.additional_relation_return_types', [ Config::set('ide-helper.additional_relation_return_types', [
'testToAnyRelation' => 'many', 'testToAnyRelation' => 'many',
'testToAnyMorphedRelation' => 'morphTo', 'testToAnyMorphedRelation' => 'morphTo',
'testToBadlyNamedNotManyRelation' => 'one',
]); ]);
} }
@@ -6,6 +6,7 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Traits
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToAnyMorphedRelationType; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToAnyMorphedRelationType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToAnyRelationType; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToAnyRelationType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToBadlyNamedNotManyRelationType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToManyRelationType; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToManyRelationType;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToOneRelationType; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types\SampleToOneRelationType;
@@ -34,4 +35,10 @@ trait HasTestRelations
$instance = $this->newRelatedInstance($related); $instance = $this->newRelatedInstance($related);
return new SampleToAnyMorphedRelationType($instance->newQuery(), $this); return new SampleToAnyMorphedRelationType($instance->newQuery(), $this);
} }
public function testToBadlyNamedNotManyRelation($related)
{
$instance = $this->newRelatedInstance($related);
return new SampleToBadlyNamedNotManyRelationType($instance->newQuery(), $this);
}
} }
@@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Types;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Concerns\SupportsDefaultModels;
use Illuminate\Database\Eloquent\Relations\Relation;
/**
* Sample for custom relation
*
* the relation is a big fake and only for testing of the docblock generation
*
* @package Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations
*/
class SampleToBadlyNamedNotManyRelationType extends Relation
{
use SupportsDefaultModels;
public function addConstraints()
{
// Fake
}
public function addEagerConstraints(array $models)
{
// Fake
}
public function initRelation(array $models, $relation)
{
// Fake
}
public function match(array $models, Collection $results, $relation)
{
// Fake
}
public function getResults()
{
// Fake
}
protected function newRelatedInstanceFor(Model $parent)
{
// Fake
}
}
@@ -57,6 +57,68 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Models; 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\CompositeBelongsToVariation
*
* @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 CompositeBelongsToVariation $bothNonNullableWithForeignKeyConstraint
* @property-read CompositeBelongsToVariation|null $nonNullableMixedWithoutForeignKeyConstraint
* @property-read CompositeBelongsToVariation|null $nullableMixedWithForeignKeyConstraint
* @method static \Illuminate\Database\Eloquent\Builder|CompositeBelongsToVariation newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|CompositeBelongsToVariation newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|CompositeBelongsToVariation query()
* @method static \Illuminate\Database\Eloquent\Builder|CompositeBelongsToVariation whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|CompositeBelongsToVariation whereNotNullColumnWithForeignKeyConstraint($value)
* @method static \Illuminate\Database\Eloquent\Builder|CompositeBelongsToVariation whereNotNullColumnWithNoForeignKeyConstraint($value)
* @method static \Illuminate\Database\Eloquent\Builder|CompositeBelongsToVariation whereNullableColumnWithForeignKeyConstraint($value)
* @method static \Illuminate\Database\Eloquent\Builder|CompositeBelongsToVariation whereNullableColumnWithNoForeignKeyConstraint($value)
* @mixin \Eloquent
*/
class CompositeBelongsToVariation extends Model
{
public $table = 'belongs_to_variations';
public function bothNonNullableWithForeignKeyConstraint(): BelongsTo
{
// Note, duplicating the keys here for simplicity.
return $this->belongsTo(
self::class,
['not_null_column_with_foreign_key_constraint', 'not_null_column_with_foreign_key_constraint'],
['not_null_column_with_foreign_key_constraint', 'not_null_column_with_foreign_key_constraint'],
);
}
public function nonNullableMixedWithoutForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(
self::class,
['not_null_column_with_foreign_key_constraint', 'not_null_column_with_no_foreign_key_constraint'],
['not_null_column_with_foreign_key_constraint', 'not_null_column_with_no_foreign_key_constraint'],
);
}
public function nullableMixedWithForeignKeyConstraint(): BelongsTo
{
return $this->belongsTo(
self::class,
['nullable_column_with_no_foreign_key_constraint', 'not_null_column_with_foreign_key_constraint'],
['nullable_column_with_no_foreign_key_constraint', 'not_null_column_with_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\ModelsOtherNamespace\AnotherModel;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Traits\HasTestRelations; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Relations\Traits\HasTestRelations;
use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Model;
@@ -97,6 +159,7 @@ use Illuminate\Database\Eloquent\Relations\MorphToMany;
* @property-read Model|\Eloquent $relationSampleToAnyMorphedRelationType * @property-read Model|\Eloquent $relationSampleToAnyMorphedRelationType
* @property-read \Illuminate\Database\Eloquent\Collection<int, Simple> $relationSampleToAnyRelationType * @property-read \Illuminate\Database\Eloquent\Collection<int, Simple> $relationSampleToAnyRelationType
* @property-read int|null $relation_sample_to_any_relation_type_count * @property-read int|null $relation_sample_to_any_relation_type_count
* @property-read Simple $relationSampleToBadlyNamedNotManyRelation
* @property-read Simple $relationSampleToManyRelationType * @property-read Simple $relationSampleToManyRelationType
* @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery() * @method static \Illuminate\Database\Eloquent\Builder|Simple newQuery()
@@ -195,4 +258,9 @@ class Simple extends Model
{ {
return $this->testToAnyMorphedRelation(Simple::class); return $this->testToAnyMorphedRelation(Simple::class);
} }
public function relationSampleToBadlyNamedNotManyRelation()
{
return $this->testToBadlyNamedNotManyRelation(Simple::class);
}
} }
@@ -9,15 +9,6 @@ use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand class Test extends AbstractModelsCommand
{ {
protected function setUp(): void
{
parent::setUp();
if (PHP_VERSION_ID < 80000) {
$this->markTestSkipped('This test requires PHP 8.0 or higher');
}
}
public function test(): void public function test(): void
{ {
$command = $this->app->make(ModelsCommand::class); $command = $this->app->make(ModelsCommand::class);
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AdvancedCastsTable extends Migration
{
public function up(): void
{
Schema::create('advanced_casts', static function (Blueprint $table) {
$table->string('cast_to_date_serialization');
$table->string('cast_to_datetime_serialization');
$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');
$table->string('cast_to_as_collection');
$table->string('cast_to_as_array_object');
});
}
}
+2 -6
View File
@@ -190,10 +190,6 @@ class MacroTest extends TestCase
public function testInitPhpDocParamsWithUnionTypes(): void 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' $phpdoc = (new MacroMock())->getPhpDoc(eval(<<<'PHP'
return new ReflectionFunction( return new ReflectionFunction(
/** /**
@@ -231,7 +227,7 @@ class MacroTest extends TestCase
{ {
$reflectionMethod = new \ReflectionMethod(UrlGeneratorMacroClass::class, '__invoke'); $reflectionMethod = new \ReflectionMethod(UrlGeneratorMacroClass::class, '__invoke');
$macro = new Macro($reflectionMethod, UrlGenerator::class, new \ReflectionClass(UrlGenerator::class), 'macroName'); $macro = new Macro($reflectionMethod, UrlGenerator::class, new ReflectionClass(UrlGenerator::class), 'macroName');
$this->assertInstanceOf(Macro::class, $macro); $this->assertInstanceOf(Macro::class, $macro);
} }
@@ -243,7 +239,7 @@ class MacroTest extends TestCase
{ {
$reflectionMethod = new \ReflectionMethod(UrlGeneratorMacroClass::class, '__invoke'); $reflectionMethod = new \ReflectionMethod(UrlGeneratorMacroClass::class, '__invoke');
$macro = new Macro($reflectionMethod, 'URL', new \ReflectionClass(UrlGenerator::class), 'macroName'); $macro = new Macro($reflectionMethod, 'URL', new ReflectionClass(UrlGenerator::class), 'macroName');
$output = <<<'DOC' $output = <<<'DOC'
/** /**
* *
+90
View File
@@ -0,0 +1,90 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests;
use Barryvdh\LaravelIdeHelper\Generator;
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
use PhpParser\Lexer\Emulative;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Parser\Php7;
class RealTimeFacadesTest extends TestCase
{
public function testRealTimeFacades()
{
// Add the views path to the view finder so the generator actually generates the file
View::addLocation(__DIR__ . '/../resources/views');
// Clear cached real-time facades
$cachedFacades = glob(storage_path('framework/cache/facade-*.php'));
foreach ($cachedFacades as $cachedFacade) {
unlink($cachedFacade);
}
// Copy stubs to storage path as if the real-time facades were cached by the framework
copy(
__DIR__ . '/stubs/facade-0e0385307adf5db34c7986ecbd11646061356ec8.php',
storage_path('framework/cache/facade-0e0385307adf5db34c7986ecbd11646061356ec8.php')
);
copy(
__DIR__ . '/stubs/facade-9431b04ec1494fc71a1bc848f020044aba2af7b1.php',
storage_path('framework/cache/facade-9431b04ec1494fc71a1bc848f020044aba2af7b1.php')
);
// new instance of the generator which we test
$generator = new Generator($this->app['config'], $this->app['view'], null, false);
// Clear aliases and macros to have a small output file
AliasLoader::getInstance()->setAliases([]);
Request::flushMacros();
// Generate the helper file and return the content
$content = $generator->generate();
$this->assertStringContainsString('namespace Facades\Illuminate\Foundation\Exceptions {', $content, 'Could not find Facades\Illuminate\Foundation\Exceptions namespace in the generated helper file.');
$this->assertStringContainsString('namespace Facades\App\Exceptions {', $content, 'Could not find Facades\App\Exceptions namespace in the generated helper file.');
$parsed = collect((new Php7(new Emulative()))->parse($content) ?: []);
// test the Facades\Illuminate\Foundation\Exceptions namespace in the generated helper file
$frameworkExceptionsNamespace = $parsed->first(function ($stmt) {
return ($stmt instanceof Namespace_) && $stmt->name->toString() === 'Facades\Illuminate\Foundation\Exceptions';
});
$this->assertNotNull($frameworkExceptionsNamespace, 'Could not find Facades\Illuminate\Foundation\Exceptions namespace');
$this->assertSame('Facades\Illuminate\Foundation\Exceptions', $frameworkExceptionsNamespace->name->toString());
$this->verifyNamespace($frameworkExceptionsNamespace, 'Illuminate\Foundation\Exceptions\Handler');
// test the Facades\App\Exceptions namespace in the generated helper file
$appExceptionsNamespace = $parsed->first(function ($stmt) {
return ($stmt instanceof Namespace_) && $stmt->name->toString() === 'Facades\App\Exceptions';
});
$this->assertNotNull($appExceptionsNamespace, 'Could not find Facades\App\Exceptions namespace');
$this->assertSame('Facades\App\Exceptions', $appExceptionsNamespace->name->toString());
$this->verifyNamespace($appExceptionsNamespace, 'App\Exceptions\Handler');
}
private function verifyNamespace(Namespace_ $namespace, $target)
{
$stmts = collect($namespace->stmts);
$this->assertInstanceOf(Class_::class, $stmts[0], 'Expected instance of Class_');
$statement = $stmts[0];
$this->assertArrayHasKey('comments', $statement->getAttributes());
$this->assertStringContainsString('@mixin \\' . $target, $statement->getAttributes()['comments'][0]->getText(), 'Mixin comment not found');
$this->assertSame(class_basename($target), $statement->name->toString(), 'Class name not found');
$this->assertSame($target, $statement->extends->toString(), 'Class extends not found');
}
protected function getPackageProviders($app)
{
return [IdeHelperServiceProvider::class];
}
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Facades\Illuminate\Foundation\Exceptions;
use Illuminate\Support\Facades\Facade;
/**
* @see \Illuminate\Foundation\Exceptions\Handler
*/
class Handler extends Facade
{
/**
* Get the registered name of the component.
*/
protected static function getFacadeAccessor(): string
{
return 'Illuminate\Foundation\Exceptions\Handler';
}
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Facades\App\Exceptions;
use Illuminate\Support\Facades\Facade;
/**
* @see \App\Exceptions\Handler
*/
class Handler extends Facade
{
/**
* Get the registered name of the component.
*/
protected static function getFacadeAccessor(): string
{
return 'App\Exceptions\Handler';
}
}