Compare commits

..
129 Commits
Author SHA1 Message Date
Stephen Jason Wang cc26683e8e fix: strip redundant builder @param tags for optional scalar defaults (#1785) 2026-08-04 14:28:01 +02:00
Thomas GnandtandClaude Opus 4.7 5060909c37 fix: Don't force |null on BelongsTo relations using ->withTrashed() (#1783)
When the related model uses SoftDeletes, `isRelationNullable()` was
forcing the BelongsTo relation type to nullable even if the relation
explicitly opted into trashed parents via `->withTrashed()`. Combined
with a NOT NULL FK column and DB-level FK constraint, the relation is
effectively non-nullable.

The SoftDeletes branch is now skipped when the relation has removed
the `SoftDeletingScope` and added no constraint on the qualified
`deleted_at` column. `->onlyTrashed()` and `->withoutTrashed()` keep
their nullable annotation because they restrict the parent to a
specific soft-delete state.

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-08-04 14:27:14 +02:00
Barry vd. Heuvel 3a53071650 Remove Laravel 11.x from integration test matrix 2026-07-10 10:50:01 +02:00
Youssef MansourandYoussef Mansour 04c8d5dd2f Add regression test for unique() column type resolution (#1787)
Confirms string()->unique() correctly resolves to string type.
Related to barryvdh/laravel-ide-helper#1747

Co-authored-by: Youssef Mansour <[email protected]>
2026-07-10 10:41:38 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 3f742fd665 Bump actions/checkout from 6 to 7 in the deps group (#1792)
Bumps the deps group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: deps
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-10 10:41:00 +02:00
Adedayo Oyetoke e1b1ef74aa Skip incrementEach and decrementEach for Eloquent helper (#1782) 2026-05-06 11:53:44 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 7fd3b7aff0 Bump release-drafter/release-drafter from 6 to 7 in the deps group (#1776)
Bumps the deps group with 1 update: [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter).


Updates `release-drafter/release-drafter` from 6 to 7
- [Release notes](https://github.com/release-drafter/release-drafter/releases)
- [Commits](https://github.com/release-drafter/release-drafter/compare/v6...v7)

---
updated-dependencies:
- dependency-name: release-drafter/release-drafter
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: deps
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-01 12:40:07 +02:00
Barry vd. Heuvel 1002cb567d Update branch alias from 3.6-dev to 3.7-dev 2026-03-17 16:27:12 +01:00
barryvdh 3e0c12ee0e Update CHANGELOG 2026-03-17 15:27:00 +00:00
Jesper Noordsij ad7e37676f Bump branch alias to 3.6 (#1774) 2026-03-17 15:12:51 +01:00
Barry vd. Heuvel 9027494e97 Remove closure in attribute test (#1772) 2026-03-05 21:35:19 +01:00
isaackaara c3f795e909 fix: skip autoload exception when class existence is being checked (#1764)
The MetaCommand's custom autoloader throws a ReflectionException for any
class not found during meta generation. However, this breaks libraries
that use class_exists() to check for optional dependencies before loading
them (e.g. Doctrine ORM checking for StaticReflectionService removed in
doctrine/persistence v4).

This fix checks the call stack (up to 3 frames) for class_exists(),
interface_exists(), trait_exists(), or enum_exists() calls and returns
gracefully instead of throwing, allowing the existence check to return
false as expected.

Fixes #1750
2026-03-04 17:15:04 +01:00
2fba281b14 Fix PHPDoc placement before PHP 8 class attributes and add regression tests (#1769)
* Initial plan

* Add tests for PHP class attribute placement and fix whitespace in regex

Co-authored-by: barryvdh <[email protected]>

* Add advanced edge case tests: final+nested-brackets and multiple attributes

Co-authored-by: barryvdh <[email protected]>

* Add PHP 8.5 test for closures inside attribute arguments

Co-authored-by: barryvdh <[email protected]>

* composer fix-style

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: barryvdh <[email protected]>
Co-authored-by: laravel-ide-helper <[email protected]>
2026-03-04 12:00:42 +01:00
laravel-ide-helper 39bcb60b7c composer fix-style 2026-03-04 09:53:06 +00:00
isaackaara 16f685aa9f fix: don't extend root's parent class for facade stubs in helper file (#1766)
Since #1674 (commit 09623f2), the generated IDE helper file adds an
`extends` declaration for Macroable classes to expose inherited methods.
However, the check to skip facades only excluded classes in the
`\Illuminate\Support\Facades` namespace, so third-party facades (e.g.
`Spatie\Menu\Laravel\Facades\Menu`) would incorrectly extend the
facade root's parent class.

This caused the generated stub to conflict with the real facade class:

    namespace Spatie\Menu\Laravel\Facades {
        class Menu extends \Spatie\Menu\Menu {  // wrong!

The fix checks whether the extends class IS a Facade subclass (using
`is_subclass_of`) rather than comparing namespace strings. This
correctly excludes ALL facade classes (both Laravel and third-party)
while still allowing non-facade Macroable classes to extend their
parent.

Fixes #1724
2026-03-04 10:52:43 +01:00
isaackaara 392d5cfc80 fix: place PHPDoc before class attributes when writing to models (#1765)
When using --write/-W with models that have PHP 8 attributes (e.g.
#[ObservedBy(...)]), the generated PHPDoc block was inserted between
the attribute and the class declaration:

    #[ObservedBy([ImageObserver::class])]
    /**
     * @property string $name
     */
    final class Image extends Model

This breaks PHPStan/Larastan because the docblock must come before
the attributes. The fix scans backward from the class declaration to
find any preceding PHP 8 attributes and inserts the docblock before
them:

    /**
     * @property string $name
     */
    #[ObservedBy([ImageObserver::class])]
    final class Image extends Model

Fixes #1734
2026-03-04 10:52:03 +01:00
isaackaara 3d8fccd700 fix: correct indentation in camel case config comment block (#1767)
The 'Support for camel cased models' comment block used 5-space
indentation for the pipe characters instead of the standard 4 spaces
used by all other comment blocks in the config file.

Fixes #1761
2026-03-04 10:48:05 +01:00
Laravel ShiftandBarry vd. Heuvel e9b62b6ee0 Laravel 13.x Compatibility (#1763)
* Bump dependencies for Laravel 13

* Update GitHub Actions for Laravel 13

* Update workflow to remove cron schedule

Removed cron schedule and adjusted pull request branches.

* Fix syntax, modify Laravel version in run-tests.yml

Updated Laravel version from '13.0' to '13.x' in CI configuration.

* Update test-ci command in composer.json

* Fix test-ci command in composer.json

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
2026-02-21 09:54:37 +01:00
Pieter Willekens 37dfcf9e14 chore: update test snapshots after dependency upgrade (#1762) 2026-02-20 14:51:32 +01:00
Pieter Willekens 573c9f3f57 ci: add PHP 8.5 to integration test matrix (#1760)
The unit test matrix already includes PHP 8.5 but it was missing
from the integration tests.
2026-02-11 11:45:28 +01:00
Pieter Willekensandlaravel-ide-helper fd3cfbe09d feat(ModelsCommand): add configuration option to disable model query methods (#1692)
* feat(ModelsCommand): add configuration option to disable model query methods

* composer fix-style

* update comment

* revert whitespace changes

* composer fix-style

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2026-02-11 11:45:10 +01:00
Pieter Willekens 3bd70eafd0 fix: wrap bare intersection types in parentheses when adding nullable (#1756)
When a MorphTo relation's docblock uses a bare intersection type in the
generic parameter (e.g. `MorphTo<BaseModel&CanBeAssigned, $this>`), the
generated property type incorrectly becomes `BaseModel&CanBeAssigned|null`
instead of the correct `(BaseModel&CanBeAssigned)|null`.

This adds `wrapIntersectionType()` which ensures bare intersection types
are wrapped in parentheses before `|null` is appended, producing valid
DNF type syntax. Applied in both `setProperty()` and `applyNullability()`.
2026-02-10 15:19:29 +01:00
Pieter Willekens 5aca186d92 perf: reduce redundant lookups and file I/O in generation hot paths (#1757)
- Convert usedMethods from list to hash map for O(1) dedup lookups
- Cache getRelationTypes() and getRelationReturnTypes() per command run
- Reuse SplFileObject instances by filename instead of re-opening per method
- Cache ContextFactory results per declaring class across docblock helpers

Benchmarked with hyperfine (10 runs, 2 warmup): 1.37x faster (~27%).
2026-02-10 15:18:58 +01:00
barryvdh 148f956272 normalize composer.json 2026-02-07 13:33:03 +00:00
Barry vd. Heuvel cff155b0d3 Replace psalm with larastan (#1755)
* Use larastan instead of psalm

* Fix cs

* Fix baseline
2026-02-07 14:32:53 +01:00
SavaandSava Markovic d87ec5835d Support all DNF types from PHP RFC (#1751)
* Support all DNF types from PHP RFC

* Update failing test

---------

Co-authored-by: Sava Markovic <[email protected]>
2026-01-26 14:24:36 +01:00
Evan Burrell b0dcd7c62f Make Soft Deleted Relationships Nullable (#1749) 2026-01-11 14:34:18 +01:00
JaspurandBarry vd. Heuvel 2ac73f2953 Skip calling fake() when required parameters exist (Socialite compatibility) (#1746)
* Skip calling fake() on facades that require parameters (fix Socialite error)

The ide-helper attempted to invoke Facade::fake() unconditionally, which
breaks when a facade (such as Laravel Socialite) requires a mandatory
parameter in its fake() method signature. This resulted in an
ArgumentCountError during `php artisan ide-helper:generate`.

This patch adds a ReflectionMethod check and skips calling fake() when
required parameters are present, ensuring compatibility with Socialite
and preserving expected behavior for facades that support parameterless
faking.

* Update Alias.php

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
2025-12-10 10:31:15 +01:00
Barry vd. Heuvel c95825a745 Fix test 2025-12-10 10:16:51 +01:00
barryvdh db606f30d1 Update CHANGELOG 2025-12-10 09:12:44 +00:00
Yannick Schinko b106f7ee85 Add support for decimal column type (#1739)
* Add support for `decimal` column type

Previously `decimal` columns were represented with a cast. This now is no longer the case and instead the column type itself is `decimal`. This change takes that into account.

* The column type may also be `numeric`

* Update tests
2025-12-10 10:11:07 +01:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> d61d20357e Bump the deps group across 1 directory with 2 updates (#1743)
Bumps the deps group with 2 updates in the / directory: [actions/checkout](https://github.com/actions/checkout) and [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action).


Updates `actions/checkout` from 5 to 6
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

Updates `stefanzweifel/git-auto-commit-action` from 6 to 7
- [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases)
- [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stefanzweifel/git-auto-commit-action/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: deps
- dependency-name: stefanzweifel/git-auto-commit-action
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: deps
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-10 10:10:25 +01:00
Jesper Noordsij e9283c40a2 Remove calls to PHP 8.5-deprecated setAccessible (#1744)
These are no-op as of PHP 8.1 and thus no longer needed

See also https://wiki.php.net/rfc/make-reflection-setaccessible-no-op
2025-12-10 10:10:09 +01:00
WentTheFox 12a0d7d54c Fix alias fake error (#1745)
* Add catch block in Alias::detectFake

* Update Alias unit tests
2025-12-10 10:09:42 +01:00
Serhii Petrov 9ef25f60e7 Add php 8.5 support (#1735) 2025-10-01 15:12:26 +02:00
Keita Shima 8dff6bb5ff Fix typos in documentation and code comments (#1733)
- Fixed 'seperate' to 'separate' in README.md (2 occurrences)
- Fixed 'columname' to 'columnname' in README.md (2 occurrences)
- Fixed 'Wether' to 'Whether' in src/Method.php PHPDoc comments (2 occurrences)
2025-09-15 14:14:26 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> 53854ea7d9 Bump actions/checkout from 4 to 5 in the deps group (#1731)
Bumps the deps group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 4 to 5
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: deps
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-02 16:32:00 +02:00
Peter Jørgensen 1fa84a3b65 fix methodsto typo (#1723) 2025-08-04 15:07:10 +02:00
barryvdh 5e973967a7 Update CHANGELOG 2025-07-18 14:10:29 +00:00
erikn69andJoel Stein 8d00250cba Add magic *_exists properties (#1712)
* Added magic `*_exists` properties.

* fix return datatype

* disabled by default

---------

Co-authored-by: Joel Stein <[email protected]>
2025-07-17 22:11:57 +02:00
Barry vd. Heuvel 877f5ed0a7 Trim strings and bump reflection docblock (#1721)
* Update version restraint

* UPdate snapshots

* update strings
2025-07-17 08:20:10 +02:00
Pieter Willekens fafe264b3c fix(ModelsCommand): use 'string' as realType for 'encrypted' casts (#1698) 2025-07-17 08:06:36 +02:00
erikn69 108f6269c1 Update .gitattributes - avoid all .php-cs-fixer files on vendor (#1708) 2025-07-17 08:02:50 +02:00
dependabot[bot]anddependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> c488262f97 Bump stefanzweifel/git-auto-commit-action from 5 to 6 in the deps group (#1719)
Bumps the deps group with 1 update: [stefanzweifel/git-auto-commit-action](https://github.com/stefanzweifel/git-auto-commit-action).


Updates `stefanzweifel/git-auto-commit-action` from 5 to 6
- [Release notes](https://github.com/stefanzweifel/git-auto-commit-action/releases)
- [Changelog](https://github.com/stefanzweifel/git-auto-commit-action/blob/master/CHANGELOG.md)
- [Commits](https://github.com/stefanzweifel/git-auto-commit-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: stefanzweifel/git-auto-commit-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: deps
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-07-17 08:02:35 +02:00
Barry vd. Heuvel 83e3f39b39 Fix tests on windows (#1720)
* Update run-tests.yml

* Update snapshot

* Simplify matrix
2025-07-17 08:02:25 +02:00
laravel-ide-helper 31979ed485 composer fix-style 2025-07-17 05:52:57 +00:00
erikn69 6d96f0bd1a Support other OS on tests (#1715) 2025-07-17 07:52:32 +02:00
Kristijan cff244b9b5 fix: Fixed wrong doc for softDeletes withTrashed method (#1688) 2025-07-17 07:50:16 +02:00
laravel-ide-helper 4dfc08a854 composer fix-style 2025-07-17 05:24:13 +00:00
erikn69 6c4ba2ad38 Support AsCollection::of($map), AsCollection::using($class, $map) (#1714)
* Support `AsCollection::of($map)`, `AsCollection::using($class, $map)`

* test compatibility(`of($map)`,`using($class, $map)` compiled), only laravel `>=12.10`
2025-07-17 07:23:52 +02:00
Robin Rosiers 1815a6140a Add multi-level directory support for translation files (#1718) 2025-06-30 13:27:12 +02:00
Santos VilanculosandMarkus Podar 7c469e4073 docs(readme): add Laravel 12 support information (#1717)
* docs(readme): add Laravel 12 support information

* docs(readme): update Laravel support information

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

---------

Co-authored-by: Markus Podar <[email protected]>
2025-06-30 13:09:32 +02:00
laravel-ide-helper ae0a06d406 composer fix-style 2025-05-15 07:57:32 +00:00
erikn69 e0b2f86326 Configurable macro return type defaults (#1711) 2025-05-15 09:56:31 +02:00
laravel-ide-helper cdefaeed42 composer fix-style 2025-05-14 18:21:46 +00:00
erikn69 c775aec1bb Revert #1629 - _Allow adding custom Macroable classes_ (#1707)
* Revert #1629 - Allow adding custom Macroable classes

* Add Macros test
2025-05-14 20:21:17 +02:00
Marvin Naumann 0fa96e572d detect default parameter from type float and adding tests for scopes with typed parameters (#1697) 2025-04-22 15:31:43 +02:00
8eec4f1ff5 feat(ModelsCommand): add support for the new Scope attribute (#1694)
* composer fix-style

* feat(model): add support for new Scope attribute

* composer fix-style

* snapshot

* check if class_exists for psalm

* check if class_exists for psalm

* resolve psalm issue

* remove prefix slash

* revert whitespace changes

* composer fix-style

* Update composer.json

---------

Co-authored-by: laravel-ide-helper <[email protected]>
Co-authored-by: Barry vd. Heuvel <[email protected]>
2025-04-04 16:14:00 +02:00
Pieter Willekens d567978ebe chore(meta): ignore abstracts in the autoloader (Fixes #1671) (#1686) 2025-02-27 20:19:11 +01:00
KentarouTakeda 09623f216c Add extends declaration for Macroable classes to fix missing inherited methods (#1674) 2025-02-22 16:59:08 +01:00
Tom Westrick 3d4e37798e Fix AsArrayObject cast to be Laravel's ArrayObject (#1675) 2025-02-22 15:16:07 +01:00
barryvdh 713a4758f9 Update CHANGELOG 2025-02-21 15:09:51 +00:00
barryvdh 8d441ec99f normalize composer.json 2025-02-11 13:59:46 +00:00
dba153f1e0 Laravel 12 support (#1672)
* Laravel 12 support

* Update run-integration-tests.yml to include Laravel 12

* Update run-tests.yml to include Laravel 12

* allow testbench ^10

* Update run-integration-tests.yml

* Update run-tests.yml

* Update composer.json

Co-authored-by: KentarouTakeda <[email protected]>

* Update run-integration-tests.yml

* Update run-tests.yml

---------

Co-authored-by: KentarouTakeda <[email protected]>
Co-authored-by: Barry vd. Heuvel <[email protected]>
2025-02-11 14:59:28 +01:00
yparitcher 40485e7350 Fix MorphTo Model Doc Generation (#1668)
fix:
```php
    /**
     * @return MorphTo<contravariant ModelA|ModelB, $this>
     */
    public function reminderable(): MorphTo {
        return $this->morphTo(__FUNCTION__, 'entity', 'entity_id');
    }
```
From:
```php
* @property-read contravariant ModelA|ModelB $reminderable
 ```
To:
```php
* @property-read ModelA|ModelB $reminderable
 ```
Fixes: #1667
2025-02-04 17:49:42 +01:00
skyler544andSkyler Mayfield ead24b10f6 prevent generation of incorrect property annotation (#1665)
Co-authored-by: Skyler Mayfield <[email protected]>
2025-01-29 22:43:43 +01:00
Kristijan 187ce9beeb Update configs.php (#1662)
Fixes config items incorectlly set as `string` due to `'array(...)'` value passed to `gettype` instead of real array
2025-01-23 18:56:10 +01:00
barryvdh 6b43ac616d Update CHANGELOG 2025-01-14 09:07:42 +00:00
Barry vd. Heuvel 980a87e250 Check if returnType from docblock is not null (#1658) 2025-01-14 10:07:00 +01:00
Barry vd. Heuvelandlaravel-ide-helper 299a81b9bb Convert auth() helper to use Auth facade (#1656)
* Convert auth() helper to use Auth facade

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2025-01-08 13:50:47 +01:00
barryvdh 06c1fab9b5 Update CHANGELOG 2025-01-08 10:03:44 +00:00
Casper Bloemendaal 271682a2a6 feat: use generics of return type to determine resulting models (#1653) 2025-01-08 11:01:30 +01:00
Barry vd. Heuvelandlaravel-ide-helper bb1caed698 Check if macro is valid (#1655)
* Check if macro is valid

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2025-01-08 11:00:53 +01:00
Barry vd. Heuvelandlaravel-ide-helper 55268cd47a Catch meta, tweak auth (#1654)
* Catch meta, tweak auth

* composer fix-style

* Catch meta, tweak auth

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2025-01-08 11:00:41 +01:00
barryvdh 088968c3fc Update CHANGELOG 2025-01-06 15:42:30 +00:00
Barry vd. Heuvel 655e057626 Fix empty closure 2025-01-06 16:29:58 +01:00
barryvdh c8d85a0d34 Update CHANGELOG 2025-01-06 14:09:45 +00:00
Barry vd. Heuvel 4c656cc71d Remove duplicate config, fix ->can() (#1650) 2025-01-06 15:08:47 +01:00
barryvdh 0fe9774dfb Update CHANGELOG 2025-01-06 13:53:52 +00:00
Jesper Noordsij 030f6f1f35 Add special 'dev' to composer keywords (#1649) 2025-01-03 15:21:18 +01:00
laravel-ide-helper 154300fb06 composer fix-style 2025-01-01 12:17:43 +00:00
Mathieu TUDISCOandBarry vd. Heuvel 05d9c3cd65 Allow adding custom Macroable classes. (#1629)
* Allow adding custom Macroable classes.

* Remove duplication, tweak config

* Remove test

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
2025-01-01 13:17:20 +01:00
Barry vd. Heuvelandlaravel-ide-helper beda399e7e Feat guess macro types (#1648)
* Add default class for HttpClient macro

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2025-01-01 10:44:43 +01:00
Nereo Berardozzi 0bbca8f5e2 Fixed generating PHPDoc for methods with class templates (#1647)
* Fixed generating PHPDoc for methods with class templates

* Fix style

* Use getter for alias template names

* Fixed missing class in static analysis
2024-12-31 17:18:28 +01:00
Barry vd. Heuvelandlaravel-ide-helper f8381565ad Add more metadata (#1646)
* Add more metadata

* composer fix-style

* Parse .env

* composer fix-style

* Check if .env exists

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-30 15:06:36 +01:00
Barry vd. Heuvel cffcd07e98 Also add eloquent template tags from base class (#1645) 2024-12-30 12:58:28 +01:00
Barry vd. Heuvelandlaravel-ide-helper 9c6380da4a Remove php parser (#1644)
* Remove php parser

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-30 12:42:35 +01:00
Barry vd. Heuvelandlaravel-ide-helper e509790805 Use forked ContextFactory (#1643)
* Use forked ContextFactory

* composer fix-style

* Trim slashes

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-30 12:03:45 +01:00
Barry vd. Heuvelandlaravel-ide-helper 791ba555f4 Add meta override for user return types (#1642)
* Add meta override for user return types

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-30 10:31:30 +01:00
Barry vd. Heuvel c324438731 Bump to 3.5 2024-12-29 19:44:30 +01:00
Barry vd. Heuvelandlaravel-ide-helper 4e4da0825a Add phpstorm meta argument hints (#1640)
* Add phpstorm meta hints

* composer fix-style

* Use php-templates from vs-code

* Fix view instance

* composer fix-style

* Add translations

* composer fix-style

* map to string

* composer fix-style

* Fix test

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-29 19:44:08 +01:00
barryvdh 66ea6aa57b Update CHANGELOG 2024-12-29 13:26:59 +00:00
Barry vd. Heuvel 2a41415f01 Add TLDR section, update options (#1639) 2024-12-29 13:10:58 +01:00
Barry vd. Heuvelandlaravel-ide-helper a7fc73e8e4 Format IDE helper (#1638)
* Format IDE helper

* composer fix-style

* Format IDE helper

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-29 12:56:55 +01:00
Barry vd. Heuvelandpb30 f343efa140 Pb30 version var rename (#1637)
* Update view "version" variable name to avoid potential conflicts

* Remove version

---------

Co-authored-by: pb30 <[email protected]>
2024-12-29 12:11:06 +01:00
Barry vd. Heuvel 0fa0403e57 Check if alias is valid 2024-12-29 12:03:42 +01:00
Barry vd. Heuvel 97e3b5eaa0 Bump to 3.4 2024-12-29 11:57:30 +01:00
Martin Tillmann 99554aef3c Update README.md (#1587)
Added Laravel 11 path to readme to make installation easier for newer installs
2024-12-29 11:52:58 +01:00
hms5232 0b2eeb4627 Make all "note" in README apply quote style (#1590)
Now, those "note" stand out in text sea
2024-12-29 11:52:24 +01:00
Lau Ernebjerg Josefsen 4ad74ba241 Add weak generics for array type objects (#1621) 2024-12-29 11:52:05 +01:00
Barry vd. Heuvelandlaravel-ide-helper 347483be03 Add option for only eloquent (#1636)
* Add option for only eloquent

* composer fix-style

* Add snapshot

* Add option to models command

* composer fix-style

* Fix namespace

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-29 11:51:09 +01:00
Barry vd. Heuvelandlaravel-ide-helper 14fdb572bb Update testsuite for Generator, simplify service provider and mock (#1635)
* Add templates to Eloquent

* Test generator, update constructor

* composer fix-style

* Add filename test

* UPdate test

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-28 19:56:36 +01:00
Barry vd. Heuvel c33eacdefc Add templates to Eloquent (#1634) 2024-12-28 11:17:17 +01:00
Yifans_ZandYifan Zhao a25b947851 fix: add @template TModel of static for Eloquent (#1631)
* fix: add @template TModel of static for Eloquent

* fix: use `===` instead of `==`

---------

Co-authored-by: Yifan Zhao <[email protected]>
2024-12-26 12:51:40 +01:00
barryvdh 59eb26742b Update CHANGELOG 2024-12-18 08:36:48 +00:00
Lito b7675670f7 Assign $output method parameter to $this->output on Generator (#1623)
The `$output` method parameter is not being assigned to the `$this->output` class variable.
2024-12-18 09:24:19 +01:00
Jeppe Knockaertandlaravel-ide-helper edb56754d1 Fix PHP 8.4 deprecations (#1618)
* Replace implicitly nullable parameters for PHP 8.4

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-12-14 11:06:34 +01:00
laravel-ide-helper 4d2728c4f0 composer fix-style 2024-12-09 15:03:45 +00:00
Lito 5da09fad67 Fixed PHP 8.4 deprecation warning (#1622)
* Fixed PHP 8.4 deprecation warning

* Fix PHP 8.4 deprecation warning
2024-12-09 16:03:20 +01:00
Bob Conan 788e72e7dd Updated CHANGELOG.md, fix typo(s) (#1613) 2024-11-13 22:38:24 +01:00
Carl-Philipp Jungandlaravel-ide-helper e3ec773374 Improve replacement of return type for methods from Query\Builder (#1575)
* Add return type `|static` to Query\Builder methods (#1574)

* Replace return type Query\Builder with Eloquent\Builder (#1574)

* Replace return type Query\Builder only for facade Eloquent (#1574)

* Add special return type replacement for Macros of \Eloquent

* Restrict special return type to methods from Eloquent\Builder and Query\Builder

* Do not overwrite return type in normalizeReturn() with conditional call of setType()

* Use generic return type for builder methods in \Eloquent

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-10-31 15:47:27 +01:00
Barry vd. Heuvel 64588afe8b Update snapshots 2024-10-31 15:39:02 +01:00
Jeramy Hing 472c1bad5d Feature: Add Config Option to Enforce Nullable Relationships (#1580)
* feat: add config option for nullable relationships

Introduce enforce_nullable_relationships configuration to control nullable Eloquent relationships.

* refactor: update logic for nullable relationships

Update isRelationNullable method to respect new config option.

* test: add tests for nullable relationship config

- Verify behavior of enforce_nullable_relationships configuration option.
- Create snapshot to reflect enforce_nullable_relationships set to false.

* docs: improve documented context and usage

* docs: update CHANGELOG with enforce_nullable_relationships option
2024-10-30 22:35:58 +01:00
barryvdh 90e7c5ac47 Update CHANGELOG 2024-10-29 14:05:10 +00:00
Pieter Willekensandlaravel-ide-helper 07e3bd8796 fix(pivot): only use unique classes in the pivot union (Fixes #1606) (#1607)
* fix(pivot): only use unique classes in the pivot union (Fixes #1606)

* composer fix-style

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-10-29 15:00:16 +01:00
Pieter Willekens 7c2fa01d36 docs(pr): remove the changelog checklist item (#1608) 2024-10-29 10:37:06 +01:00
Barry vd. Heuvel 75b05a9f74 Create release-drafter.yml 2024-10-28 15:17:58 +01:00
Barry vd. Heuvel 71aa6a6cf3 Create update-changelog.yaml (#1605)
* Create update-changelog.yaml

* Create release-drafter.yml
2024-10-28 15:16:33 +01:00
Barry vd. Heuvel be806dc316 Update CHANGELOG.md 2024-10-28 15:11:38 +01:00
Ramon DantasandBarry vd. Heuvel 800e2347a6 feat: use numeric type on fields with decimal casts (#1583)
* feat: use `numeric` type on fields with `decimal` casts

* docs: update CHANGELOG.md

---------

Co-authored-by: Barry vd. Heuvel <[email protected]>
2024-10-28 12:32:31 +01:00
Barry vd. Heuvel 7549f58a30 Smarter reset (#1603)
* Smarter reset

* Changelog

* Fix phpdocs
2024-10-28 12:21:53 +01:00
Barry vd. Heuvel 7740b80704 Merge branch 'uno-sw-patch-1' 2024-10-28 11:54:48 +01:00
Barry vd. Heuvel d6fb7907cc Merge branch 'patch-1' of github.com:uno-sw/laravel-ide-helper into uno-sw-patch-1 2024-10-28 11:35:50 +01:00
Pieter Willekensandlaravel-ide-helper dc70337e48 feat(pivot): add support for multiple pivot types when using the same accessor (#1597)
* feat(pivot): add support for multiple pivot types when using the same accessor

* composer fix-style

* changelog

* resolve namespaces

* Revert "resolve namespaces"

This reverts commit 37559ab089.

* resolve namespaces

---------

Co-authored-by: laravel-ide-helper <[email protected]>
2024-10-28 11:32:19 +01:00
KentarouTakeda 4b051e45c7 Fix the description of unused option (#1600) 2024-10-28 11:30:54 +01:00
Barry vd. Heuvel 7956ccb494 Bump dev alias to 3.2 (#1596) 2024-10-17 18:43:13 +02:00
uno-sw 07719015b3 Update CHANGELOG.md 2024-08-02 13:28:07 +09:00
uno-sw 7d2ea97f25 Update test 2024-08-02 13:21:45 +09:00
uno-sw 51fb97b8fb Add support for AsCollection::using and AsEnumCollection::of casts 2024-08-02 13:18:15 +09:00
142 changed files with 5567 additions and 1156 deletions
+1 -3
View File
@@ -6,9 +6,7 @@
/.gitattributes export-ignore
/.github export-ignore
/.gitignore export-ignore
/.php-cs-fixer.cache export-ignore
/.php-cs-fixer.php export-ignore
/.php-cs-fixer.tests.cache export-ignore
/.php-cs-fixer* export-ignore
/phpunit.xml.dist export-ignore
/psalm-baseline.xml export-ignore
/psalm.xml export-ignore
-1
View File
@@ -13,6 +13,5 @@
### Checklist
- [ ] Existing tests have been adapted and/or new tests have been added
- [ ] Add a CHANGELOG.md entry
- [ ] Update the README.md
- [ ] Code style has been fixed via `composer fix-style`
+4
View File
@@ -0,0 +1,4 @@
template: |
## Whats Changed
$CHANGES
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Git checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Validate Composer configuration
run: composer validate --strict
@@ -23,6 +23,6 @@ jobs:
composer global require ergebnis/composer-normalize
composer normalize
- uses: stefanzweifel/git-auto-commit-action@v5
- uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: normalize composer.json
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -27,7 +27,7 @@ jobs:
- run: composer fix-style
continue-on-error: true
- uses: stefanzweifel/git-auto-commit-action@v5
- uses: stefanzweifel/git-auto-commit-action@v7
with:
commit_message: composer fix-style
commit_author: laravel-ide-helper <[email protected]>
+41
View File
@@ -0,0 +1,41 @@
name: Release Drafter
on:
push:
# branches to consider in the event; optional, defaults to all
branches:
- master
# pull_request event is required only for autolabeler
pull_request:
# Only following types are handled by the action, but one can default to all as well
types: [opened, reopened, synchronize]
# pull_request_target event is required for autolabeler to support PRs from forks
# pull_request_target:
# types: [opened, reopened, synchronize]
permissions:
contents: read
jobs:
update_release_draft:
permissions:
# write permission is required to create a github release
contents: write
# write permission is required for autolabeler
# otherwise, read permission is required at least
pull-requests: write
runs-on: ubuntu-latest
steps:
# (Optional) GitHub Enterprise requires GHE_HOST variable set
#- name: Set GHE_HOST
# run: |
# echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV
# Drafts your next Release notes as Pull Requests are merged into "master"
- uses: release-drafter/release-drafter@v7
# (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
# with:
# config-name: my-config.yml
# disable-autolabeler: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+65 -61
View File
@@ -1,75 +1,79 @@
name: Integration Tests
on:
push:
branches:
- master
pull_request:
branches:
- "*"
schedule:
- cron: '0 0 * * *'
push:
branches:
- master
pull_request:
branches:
- "*"
jobs:
php-laravel-integration-tests:
runs-on: ubuntu-22.04
timeout-minutes: 15
env:
COMPOSER_NO_INTERACTION: 1
strategy:
fail-fast: false
matrix:
php: [8.4, 8.3, 8.2]
laravel: [^11.15]
php-laravel-integration-tests:
runs-on: ubuntu-22.04
name: P${{ matrix.php }} - Laravel${{ matrix.laravel }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
path: src
timeout-minutes: 15
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
env:
COMPOSER_NO_INTERACTION: 1
- name: Install dependencies
run: |
composer create-project --prefer-dist laravel/laravel:${{ matrix.laravel }} --stability=dev --no-progress sample
cd sample
composer config minimum-stability dev
composer update --prefer-stable --prefer-dist --no-progress
strategy:
fail-fast: false
matrix:
php: [8.5, 8.4, 8.3, 8.2]
laravel: [12.x, 13.x]
exclude:
- laravel: 13.x
php: 8.2
- name: Add package from source
run: |
cd sample
sed -e 's|"type": "project",|&\n"repositories": [ { "type": "path", "url": "../src" } ],|' -i composer.json
composer require --dev "barryvdh/laravel-ide-helper:*" --with-all-dependencies
name: P${{ matrix.php }} - Laravel${{ matrix.laravel }}
- name: Execute generate run
run: |
cd sample
php artisan ide-helper:generate
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
path: src
- name: Execute meta run
run: |
cd sample
php artisan ide-helper:meta -v
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- name: Check file existence
run: |
ls sample/_ide_helper.php
ls sample/.phpstorm.meta.php
- name: Install dependencies
run: |
composer create-project --prefer-dist laravel/laravel:${{ matrix.laravel }} --stability=dev --no-progress sample
cd sample
composer config minimum-stability dev
composer update --prefer-stable --prefer-dist --no-progress
- name: Add package from source
run: |
cd sample
sed -e 's|"type": "project",|&\n"repositories": [ { "type": "path", "url": "../src" } ],|' -i composer.json
composer require --dev "barryvdh/laravel-ide-helper:*" --with-all-dependencies
- name: Check logs
run: |
if [ `ls -1q "sample/storage/logs/" | wc -l` -gt 0 ]; then
for logfile in sample/storage/logs/*; do
echo "-- $logfile --"
cat $logfile
done
exit 1
fi
- name: Execute generate run
run: |
cd sample
php artisan ide-helper:generate
- name: Execute meta run
run: |
cd sample
php artisan ide-helper:meta -v
- name: Check file existence
run: |
ls sample/_ide_helper.php
ls sample/.phpstorm.meta.php
- name: Check logs
run: |
if [ `ls -1q "sample/storage/logs/" | wc -l` -gt 0 ]; then
for logfile in sample/storage/logs/*; do
echo "-- $logfile --"
cat $logfile
done
exit 1
fi
+2 -2
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -25,4 +25,4 @@ jobs:
run: composer install --prefer-dist --no-progress --no-suggest
- name: Analyze
run: composer analyze
run: vendor/bin/phpstan --no-progress --error-format=github
+35 -26
View File
@@ -7,46 +7,55 @@ on:
pull_request:
branches:
- "*"
schedule:
- cron: '0 0 * * *'
jobs:
php-tests:
runs-on: ${{ matrix.os }}
timeout-minutes: 15
env:
COMPOSER_NO_INTERACTION: 1
strategy:
fail-fast: false
matrix:
os: [ubuntu-22.04, windows-2019]
php: [8.4, 8.3, 8.2]
laravel: [^11.15]
os: [ubuntu-latest]
php: [8.5, 8.4, 8.3, 8.2]
laravel: [12.x, 13.x, ~11.15]
stability: [prefer-lowest, prefer-stable]
include:
- os: windows-latest
php: 8.3
laravel: 12.x
stability: prefer-stable
exclude:
- laravel: 13.x
php: 8.2
steps:
- name: Set git to use LF
if: ${{ matrix.os == 'windows-2019' }}
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Set git to use LF
if: "${{ matrix.os == 'windows-latest' }}"
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- name: Checkout code
uses: actions/checkout@v4
- name: Checkout code
uses: actions/checkout@v7
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
extensions: pdo_sqlite, fileinfo
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
extensions: pdo_sqlite, fileinfo
- name: Install dependencies
run: |
composer remove vimeo/psalm --no-update --dev
composer remove friendsofphp/php-cs-fixer --no-update --dev
composer require "laravel/framework:${{ matrix.laravel }}" --no-update --no-progress
composer update --prefer-dist --no-progress --${{ matrix.stability }}
- name: Install dependencies
run: |
composer remove vimeo/psalm --no-update --dev
composer remove friendsofphp/php-cs-fixer --no-update --dev
composer require "laravel/framework:${{ matrix.laravel }}" --no-update --no-progress
composer update --prefer-dist --no-progress --${{ matrix.stability }}
- name: Execute Unit Tests
run: composer test-ci
- name: Execute Unit Tests
run: composer test-ci
+34
View File
@@ -0,0 +1,34 @@
name: "Update Changelog"
on:
release:
types: [released]
jobs:
update:
runs-on: ubuntu-latest
permissions:
# Give the default GITHUB_TOKEN write permission to commit and push the
# updated CHANGELOG back to the repository.
# https://github.blog/changelog/2023-02-02-github-actions-updating-the-default-github_token-permissions-to-read-only/
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
ref: ${{ github.event.release.target_commitish }}
- name: Update Changelog
uses: stefanzweifel/changelog-updater-action@v1
with:
latest-version: ${{ github.event.release.tag_name }}
release-notes: ${{ github.event.release.body }}
- name: Commit updated CHANGELOG
uses: stefanzweifel/git-auto-commit-action@v7
with:
branch: ${{ github.event.release.target_commitish }}
commit_message: Update CHANGELOG
file_pattern: CHANGELOG.md
+2 -1
View File
@@ -1,5 +1,6 @@
.phpunit.result.cache
/auth.json
/build
/.idea
/.php-cs-fixer.cache
/.php-cs-fixer.php
+1 -1
View File
@@ -3,7 +3,7 @@ require __DIR__ . '/vendor/autoload.php';
$finder = PhpCsFixer\Finder::create()
->in(__DIR__)
->exclude('tests');
->exclude(['tests', 'build']);
$config = require __DIR__ . '/.php-cs-fixer.common.php';
+412 -124
View File
@@ -1,63 +1,315 @@
# Changelog
All notable changes to this project will be documented in this file.
## v3.7.0 - 2026-03-17
[Next release](https://github.com/barryvdh/laravel-ide-helper/compare/v3.1.0...master)
--------------
### What's Changed
* Skip calling fake() when required parameters exist (Socialite compatibility) by @Jaspur in https://github.com/barryvdh/laravel-ide-helper/pull/1746
* Make Soft Deleted Relationships Nullable by @evan-burrell in https://github.com/barryvdh/laravel-ide-helper/pull/1749
* Support all DNF types from PHP RFC by @gurmanolog in https://github.com/barryvdh/laravel-ide-helper/pull/1751
* Replace psalm with larastan by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1755
* perf: reduce redundant lookups and file I/O in generation hot paths by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1757
* fix: wrap bare intersection types in parentheses when adding nullable by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1756
* feat(ModelsCommand): add configuration option to disable model query methods by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1692
* ci: add PHP 8.5 to integration test matrix by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1760
* chore: update test snapshots after merge order issue by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1762
* Laravel 13.x Compatibility by @laravel-shift in https://github.com/barryvdh/laravel-ide-helper/pull/1763
* fix: Correct indentation in camel case config comment block by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1767
* fix: Place PHPDoc before class attributes when writing to models by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1765
* fix: Don't extend root's parent class for facade stubs in helper file by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1766
* Fix PHPDoc placement before PHP 8 class attributes and add regression tests by @Copilot in https://github.com/barryvdh/laravel-ide-helper/pull/1769
* fix: Skip autoload exception when class existence is being checked by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1764
* Remove closure in attribute test by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1772
* Bump branch alias to 3.6 by @jnoordsij in https://github.com/barryvdh/laravel-ide-helper/pull/1774
### New Contributors
* @Jaspur made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1746
* @evan-burrell made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1749
* @gurmanolog made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1751
* @laravel-shift made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1763
* @isaackaara made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1767
* @Copilot made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1769
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.6.1...v3.7.0
## v3.6.1 - 2025-12-10
### What's Changed
* Fix `methodsto` typo in README by @peterchrjoergensen in https://github.com/barryvdh/laravel-ide-helper/pull/1723
* Bump actions/checkout from 4 to 5 in the deps group by @dependabot[bot] in https://github.com/barryvdh/laravel-ide-helper/pull/1731
* Fix typos in documentation and code comments by @kei1111 in https://github.com/barryvdh/laravel-ide-helper/pull/1733
* Add php 8.5 support by @sergiy-petrov in https://github.com/barryvdh/laravel-ide-helper/pull/1735
* Fix alias fake error by @WentTheFox in https://github.com/barryvdh/laravel-ide-helper/pull/1745
* Remove calls to PHP 8.5-deprecated `setAccessible` by @jnoordsij in https://github.com/barryvdh/laravel-ide-helper/pull/1744
* Bump the deps group across 1 directory with 2 updates by @dependabot[bot] in https://github.com/barryvdh/laravel-ide-helper/pull/1743
* Add support for `decimal` column type by @BrainStone in https://github.com/barryvdh/laravel-ide-helper/pull/1739
### New Contributors
* @peterchrjoergensen made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1723
* @kei1111 made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1733
* @WentTheFox made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1745
* @BrainStone made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1739
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.6.0...v3.6.1
## v3.6.0 - 2025-07-18
### What's Changed
* fix: Change AsArrayObject cast to be Laravel's ArrayObject by @wsamoht in https://github.com/barryvdh/laravel-ide-helper/pull/1675
* Add extends declaration for Macroable classes to fix missing inherited methods by @KentarouTakeda in https://github.com/barryvdh/laravel-ide-helper/pull/1674
* fix(meta): ignore aliases in the autoloader (Fixes #1671) by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1686
* feat(ModelsCommand): add support for the new Scope attribute by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1694
* fix type change for scope default float parameter by @nivseb in https://github.com/barryvdh/laravel-ide-helper/pull/1697
* Revert #1629 - *Allow adding custom Macroable classes* by @erikn69 in https://github.com/barryvdh/laravel-ide-helper/pull/1707
* Configurable macro return type defaults by @erikn69 in https://github.com/barryvdh/laravel-ide-helper/pull/1711
* docs(readme): add Laravel 12 support information by @SantosVilanculos in https://github.com/barryvdh/laravel-ide-helper/pull/1717
* Add multi-level directory support for translation files by @RosiersRobin in https://github.com/barryvdh/laravel-ide-helper/pull/1718
* Support `AsCollection::of($map)`, `AsCollection::using($class, $map)` by @erikn69 in https://github.com/barryvdh/laravel-ide-helper/pull/1714
* fix: Fixed wrong doc for SoftDeletes `withTrashed` method by @eldair in https://github.com/barryvdh/laravel-ide-helper/pull/1688
* Support other OS on tests by @erikn69 in https://github.com/barryvdh/laravel-ide-helper/pull/1715
* Fix tests on windows by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1720
* Bump stefanzweifel/git-auto-commit-action from 5 to 6 in the deps group by @dependabot[bot] in https://github.com/barryvdh/laravel-ide-helper/pull/1719
* Update .gitattributes - avoid all `.php-cs-fixer` files on vendor by @erikn69 in https://github.com/barryvdh/laravel-ide-helper/pull/1708
* fix(ModelsCommand): use 'string' as realType for 'encrypted' casts by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1698
* Trim strings and bump reflection docblock by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1721
* Add magic *_exists properties by @erikn69 in https://github.com/barryvdh/laravel-ide-helper/pull/1712
### New Contributors
* @wsamoht made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1675
* @nivseb made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1697
* @SantosVilanculos made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1717
* @RosiersRobin made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1718
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.5.5...v3.6.0
## v3.5.5 - 2025-02-21
### What's Changed
* Fix for incorrect config item types in meta file by @eldair in https://github.com/barryvdh/laravel-ide-helper/pull/1662
* Prevent generation of incorrect property annotation by @skyler544 in https://github.com/barryvdh/laravel-ide-helper/pull/1665
* Fix MorphTo Model Doc Generation by @yparitcher in https://github.com/barryvdh/laravel-ide-helper/pull/1668
* Laravel 12 support by @jonnott in https://github.com/barryvdh/laravel-ide-helper/pull/1672
### New Contributors
* @eldair made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1662
* @skyler544 made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1665
* @yparitcher made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1668
* @jonnott made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1672
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.5.4...v3.5.5
## v3.5.4 - 2025-01-14
### What's Changed
* Convert auth() helper to use Auth facade by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1656
* Check if returnType from docblock is not null by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1658
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.5.3...v3.5.4
## v3.5.3 - 2025-01-08
### What's Changed
* Catch meta, tweak auth by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1654
* Check if macro is valid by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1655
* feat: use generics of return type to determine resulting models by @Bloemendaal in https://github.com/barryvdh/laravel-ide-helper/pull/1653
### New Contributors
* @Bloemendaal made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1653
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.5.2...v3.5.3
## v3.5.2 - 2025-01-06
### Fixes
Fix empty/anonymous closure in meta command.
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.5.1...v3.5.2
## v3.5.1 - 2025-01-06
### What's Changed
* Remove duplicate config, fix ->can() by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1650
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.5.0...v3.5.1
## v3.5.0 - 2025-01-06
### What's Changed
* Add phpstorm meta argument hints by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1640
* Add meta override for user return types by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1642
* Use forked ContextFactory by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1643
* Remove php parser by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1644
* Also add eloquent template tags from base class by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1645
* Add more metadata by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1646
* Fixed generating PHPDoc for methods with class templates by @chack1172 in https://github.com/barryvdh/laravel-ide-helper/pull/1647
* Feat guess macro types by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1648
* Allow adding custom Macroable classes. by @mathieutu in https://github.com/barryvdh/laravel-ide-helper/pull/1629
* Add special `dev` to composer keywords by @jnoordsij in https://github.com/barryvdh/laravel-ide-helper/pull/1649
### New Contributors
* @chack1172 made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1647
* @mathieutu made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1629
* @jnoordsij made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1649
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.4.0...v3.5.0
## v3.4.0 - 2024-12-29
### What's Changed
* fix: add @template TModel of static for Eloquent by @imzyf in https://github.com/barryvdh/laravel-ide-helper/pull/1631
* Add templates to Eloquent by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1634
* Update testsuite for Generator, simplify service provider and mock by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1635
* Add option for only eloquent by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1636
* Add weak generics for array type objects by @LauJosefsen in https://github.com/barryvdh/laravel-ide-helper/pull/1621
* Make all "note" in README apply quote style by @hms5232 in https://github.com/barryvdh/laravel-ide-helper/pull/1590
* Update README.md by @Mtillmann in https://github.com/barryvdh/laravel-ide-helper/pull/1587
* Rename view var by @barryvdh and @pb30 in https://github.com/barryvdh/laravel-ide-helper/pull/1637 and https://github.com/barryvdh/laravel-ide-helper/pull/1563
* Format IDE helper by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1638
* Add TLDR section, update options by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1639
### New Contributors
* @imzyf made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1631
* @LauJosefsen made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1621
* @hms5232 made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1590
* @Mtillmann made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1587
* @pb30 made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1563
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.3.0...v3.4.0
## v3.3.0 - 2024-12-18
### What's Changed
* Feature: Add Config Option to Enforce Nullable Relationships by @jeramyhing in https://github.com/barryvdh/laravel-ide-helper/pull/1580
* Improve replacement of return type for methods from Query\Builder by @pjio in https://github.com/barryvdh/laravel-ide-helper/pull/1575
* Update CHANGELOG.md, fix typo(s) by @NicholasWilsonDEV in https://github.com/barryvdh/laravel-ide-helper/pull/1613
* Fixed PHP 8.4 deprecation warning by @eusonlito in https://github.com/barryvdh/laravel-ide-helper/pull/1622
* Fix PHP 8.4 deprecations by @JeppeKnockaert in https://github.com/barryvdh/laravel-ide-helper/pull/1618
* Assign $output method parameter to $this->output on Generator by @eusonlito in https://github.com/barryvdh/laravel-ide-helper/pull/1623
### New Contributors
* @jeramyhing made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1580
* @pjio made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1575
* @NicholasWilsonDEV made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1613
* @eusonlito made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1622
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.2.2...v3.3.0
## v3.2.2 - 2024-10-29
### Whats Changed
* fix(pivot): only use unique classes in the pivot union (Fixes #1606) (#1607) @pataar
* docs(pr): remove the changelog checklist item (#1608) @pataar
* Create update-changelog.yaml (#1605) @barryvdh
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.2.1...v3.2.2
## 3.2.1 - 2024-10-28
### What's Changed
* chore: Fix the description of unused option by @KentarouTakeda in https://github.com/barryvdh/laravel-ide-helper/pull/1600
* feat(pivot): add support for multiple pivot types when using the same accessor by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1597
* Add support for `AsCollection::using` and `AsEnumCollection::of` casts by @uno-sw in https://github.com/barryvdh/laravel-ide-helper/pull/1577
* Smarter reset by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1603
* feat: use `numeric` type on fields with `decimal` casts by @ekisu in https://github.com/barryvdh/laravel-ide-helper/pull/1583
### New Contributors
* @uno-sw made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1577
* @ekisu made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1583
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.2.0...v3.2.1
## 3.2.0 - 2024-10-18
### Fixed
- Fix type of hashed model property to `string`
### Changed
- Update view "version" variable name to avoid potential conflicts
- Add support for EloquentBuilder generics introduced in Laravel 11.15.
- Drop support for Laravel versions earlier than 11.15.
### Added
2024-07-12, 3.1.0
------------------
- Introduce `enforce_nullable_relationships` configuration option to control how nullable Eloquent relationships are enforced during static analysis. This provides flexibility for scenarios where application logic ensures data integrity without relying on database constraints. [#1580 / jeramyhing](https://github.com/barryvdh/laravel-ide-helper/pull/1580)
- Add support for AsCollection::using and AsEnumCollection::of casts [#1577 / uno-sw](https://github.com/barryvdh/laravel-ide-helper/pull/1577)
## 3.1.0 - 2024-07-12
### Fixed
- Fix return value of query scopes from parent class [#1366 / sforward](https://github.com/barryvdh/laravel-ide-helper/pull/1366)
- Add static to isBuiltin() check in ide-helper:models [#1541 / bram-pkg](https://github.com/barryvdh/laravel-ide-helper/pull/1541)
- Fix for getSomethingAttribute functions which return a collection with type templating in the phpDoc. [#1567 / stefanScrumble](https://github.com/barryvdh/laravel-ide-helper/pull/1567)
### Added
- Add type to pivot when using a custom pivot class [#1518 / d3v2a](https://github.com/barryvdh/laravel-ide-helper/pull/1518)
- Add support in morphTo relationship for null values [#1547 / matysekmichal](https://github.com/barryvdh/laravel-ide-helper/pull/1547)
- Add support for AsEnumCollection casts [#1557 / Braunson](https://github.com/barryvdh/laravel-ide-helper/pull/1557)
- Support for Attribute class in attributes [#1567 / stefanScrumble](https://github.com/barryvdh/laravel-ide-helper/pull/1567)
2024-03-01, 3.0.0
------------------
- Add type to pivot when using a custom pivot class [#1518 / d3v2a](https://github.com/barryvdh/laravel-ide-helper/pull/1518)
- Add support in morphTo relationship for null values [#1547 / matysekmichal](https://github.com/barryvdh/laravel-ide-helper/pull/1547)
- Add support for AsEnumCollection casts [#1557 / Braunson](https://github.com/barryvdh/laravel-ide-helper/pull/1557)
- Support for Attribute class in attributes [#1567 / stefanScrumble](https://github.com/barryvdh/laravel-ide-helper/pull/1567)
## 3.0.0 - 2024-03-01
### Added
- Support for Laravel 11 [#1520 / KentarouTakeda](https://github.com/barryvdh/laravel-ide-helper/pull/1520)
### Changed
- Make `--reset` always keep the text and remove `--smart-reset`. Always skip the classname [#1523 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1523) & [#1525 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1525)
- Use short types (`int` and `bool` instead of `integer` and `boolean`) [#1524 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1524)
### Removed
- Support for Laravel 9 and use of doctrine/dbal [#1512 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1512)
With this functionality gone, a few changes have been made:
- support for custom datatypes has been dropped (config `custom_db_types`) unknown data types default to `string` now and to fix the type, add a proper cast in Eloquent
- You _might_ have top-level dependency on doctrine/dbal. This may have been in the past due to ide-helper, we suggest to check if you still need it and remove it otherwise
- You *might* have top-level dependency on doctrine/dbal. This may have been in the past due to ide-helper, we suggest to check if you still need it and remove it otherwise
- Minimum PHP version, due to Laravel 10, is now PHP 8.1
2024-02-15, 2.15.1
------------------
## 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
------------------
## 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)
@@ -67,10 +319,12 @@ All notable changes to this project will be documented in this file.
- 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)
@@ -80,25 +334,27 @@ All notable changes to this project will be documented in this file.
- 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
### Changed
- Official support for Lumen has been dropped [#1425 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1425)
- Refactor resolving of null information for custom casted attribute types [#1330 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1330)
### Fixed
- Catch exceptions when loading aliases [#1465 / dongm2ez](https://github.com/barryvdh/laravel-ide-helper/pull/1465)
### 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 `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
### Fixed
- Fix return type of methods provided by `SoftDeletes` [#1345 / KentarouTakeda](https://github.com/barryvdh/laravel-ide-helper/pull/1345)
- Handle PHP 8.1 deprecation warnings when passing `null` to `new \ReflectionClass` [#1351 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1351)
- Fix issue where \Eloquent is not included when using write_mixin [#1352 / Jefemy](https://github.com/barryvdh/laravel-ide-helper/pull/1352)
@@ -107,176 +363,208 @@ All notable changes to this project will be documented in this file.
- Fix Castable class if failed to detect it from return types [#1388 / kwarcu](https://github.com/barryvdh/laravel-ide-helper/pull/1388)
### Added
- Added Laravel 10 support [#1407 / lptn](https://github.com/barryvdh/laravel-ide-helper/pull/1407)
- Add support for custom casts that implement `CastsInboundAttributes` [#1329 / sforward](https://github.com/barryvdh/laravel-ide-helper/pull/1329)
- Add option `use_generics_annotations` for collection type hints [#1298 / tanerkay](https://github.com/barryvdh/laravel-ide-helper/pull/1298)
2022-03-06, 2.12.3
------------------
## 2022-03-06, 2.12.3
### Fixed
- Fix date and datetime handling for attributes that set a serialization format option for the Carbon instance [#1324 / FLeudts](https://github.com/barryvdh/laravel-ide-helper/pull/1324)
- Fix composer conflict with composer/pcre version 2/3. [#1327 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1327)
2022-02-08, 2.12.2
------------------
### Fixed
- Remove composer dependecy, use copy of ClassMapGenerator [#1313 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1313)
## 2022-02-08, 2.12.2
2022-01-24, 2.12.1
------------------
### Fixed
- Remove composer dependency, use copy of ClassMapGenerator [#1313 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1313)
## 2022-01-24, 2.12.1
### Fixed
- Properly handle `Castable`s without return type. [#1306 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1306)
2022-01-23, 2.12.0
------------------
## 2022-01-23, 2.12.0
### Added
- Add support for custom casts that using `Castable` [#1287 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1287)
- Added Laravel 9 support [#1297 / rcerljenko](https://github.com/barryvdh/laravel-ide-helper/pull/1297)
- Added option `additional_relation_return_types` for custom relations that don't fit the typical naming scheme
2022-01-03, 2.11.0
------------------
## 2022-01-03, 2.11.0
### Added
- Add support for Laravel 8.77 Attributes [\#1289 / SimonJnsson](https://github.com/barryvdh/laravel-ide-helper/pull/1289)
- Add support for Laravel 8.77 Attributes [#1289 / SimonJnsson](https://github.com/barryvdh/laravel-ide-helper/pull/1289)
- Add support for cast types `decimal:*`, `encrypted:*`, `immutable_date`, `immutable_datetime`, `custom_datetime`, and `immutable_custom_datetime` [#1262 / miken32](https://github.com/barryvdh/laravel-ide-helper/pull/1262)
- Add support of variadic parameters in `ide-helper:models` [\#1234 / shaffe-fr](https://github.com/barryvdh/laravel-ide-helper/pull/1234)
- Add support of custom casts without properties [\#1267 / sparclex](https://github.com/barryvdh/laravel-ide-helper/pull/1267)
- Add support of variadic parameters in `ide-helper:models` [#1234 / shaffe-fr](https://github.com/barryvdh/laravel-ide-helper/pull/1234)
- Add support of custom casts without properties [#1267 / sparclex](https://github.com/barryvdh/laravel-ide-helper/pull/1267)
### Fixed
- Fix recursively searching for `HasFactory` and `Macroable` traits [\#1216 / daniel-de-wit](https://github.com/barryvdh/laravel-ide-helper/pull/1216)
- Use platformName to determine db type when casting boolean types [\#1212 / stockalexander](https://github.com/barryvdh/laravel-ide-helper/pull/1212)
- Fix recursively searching for `HasFactory` and `Macroable` traits [#1216 / daniel-de-wit](https://github.com/barryvdh/laravel-ide-helper/pull/1216)
- Use platformName to determine db type when casting boolean types [#1212 / stockalexander](https://github.com/barryvdh/laravel-ide-helper/pull/1212)
### Changed
- Move default models helper filename to config [\#1241 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
2021-06-18, 2.10.1
------------------
- Move default models helper filename to config [#1241 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
## 2021-06-18, 2.10.1
### Added
- Added Type registration according to [Custom Mapping Types documentation](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#custom-mapping-types) [\#1228 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
- Added Type registration according to [Custom Mapping Types documentation](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/types.html#custom-mapping-types) [#1228 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
### Fixed
- Fixing issue where configured custom_db_types could cause a DBAL exception to be thrown while running `ide-helper:models` [\#1228 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
2021-04-09, 2.10.0
------------------
- Fixing issue where configured custom_db_types could cause a DBAL exception to be thrown while running `ide-helper:models` [#1228 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1241)
## 2021-04-09, 2.10.0
### Added
- Allowing Methods to be set or unset in ModelHooks [\#1198 / jenga201](https://github.com/barryvdh/laravel-ide-helper/pull/1198)\
- Allowing Methods to be set or unset in ModelHooks [#1198 / jenga201](https://github.com/barryvdh/laravel-ide-helper/pull/1198)
Note: the visibility of `\Barryvdh\LaravelIdeHelper\Console\ModelsCommand::setMethod` has been changed to **public**!
### Fixed
- Fixing issue where incorrect autoloader unregistered [\#1210 / tezhm](https://github.com/barryvdh/laravel-ide-helper/pull/1210)
2021-04-02, 2.9.3
-----------------
- Fixing issue where incorrect autoloader unregistered [#1210 / tezhm](https://github.com/barryvdh/laravel-ide-helper/pull/1210)
## 2021-04-02, 2.9.3
### Fixed
- Support both customized namespace factories as well as default resolvable ones [\#1201 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1201)
2021-04-01, 2.9.2
-----------------
- Support both customized namespace factories as well as default resolvable ones [#1201 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/1201)
## 2021-04-01, 2.9.2
### Added
- Model hooks for adding custom information from external sources to model classes through the ModelsCommand [\#945 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/945)
- Model hooks for adding custom information from external sources to model classes through the ModelsCommand [#945 / wimski](https://github.com/barryvdh/laravel-ide-helper/pull/945)
### Fixed
- Fix ide-helper:models exception if model doesn't have factory [\#1196 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1196)
- Running tests triggering post_migrate hooks [\#1193 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1193)
- Array_merge error when config is cached prior to package install [\#1184 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1184)
2021-03-15, 2.9.1
-----------------
- Fix ide-helper:models exception if model doesn't have factory [#1196 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1196)
- Running tests triggering post_migrate hooks [#1193 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1193)
- Array_merge error when config is cached prior to package install [#1184 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1184)
## 2021-03-15, 2.9.1
### Added
- Generate PHPDoc for Laravel 8.x factories [\#1074 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1074)
- Add a comment to a property like table columns [\#1168 / biiiiiigmonster](https://github.com/barryvdh/laravel-ide-helper/pull/1168)
- Added `post_migrate` hook to run commands after a migration [\#1163 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1163)
- Allow for PhpDoc for macros with union types [\#1148 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1148)
- Generate PHPDoc for Laravel 8.x factories [#1074 / ahmed-aliraqi](https://github.com/barryvdh/laravel-ide-helper/pull/1074)
- Add a comment to a property like table columns [#1168 / biiiiiigmonster](https://github.com/barryvdh/laravel-ide-helper/pull/1168)
- Added `post_migrate` hook to run commands after a migration [#1163 / netpok](https://github.com/barryvdh/laravel-ide-helper/pull/1163)
- Allow for PhpDoc for macros with union types [#1148 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1148)
### Fixed
- Error when generating helper for invokable classes [\#1124 / standaniels](https://github.com/barryvdh/laravel-ide-helper/pull/1124)
- Fix broken ReflectionUnionTypes [\#1132 / def-studio](https://github.com/barryvdh/laravel-ide-helper/pull/1132)
- Relative class names are not converted to fully-qualified class names [\#1005 / SavKS](https://github.com/barryvdh/laravel-ide-helper/pull/1005)
2020-12-30, 2.9.0
-----------------
### Changed
- Dropped support for Laravel 6 and Laravel 7, as well as support for PHP 7.2 and added support for doctrine/dbal:^3 [\#1114 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1114)
- Error when generating helper for invokable classes [#1124 / standaniels](https://github.com/barryvdh/laravel-ide-helper/pull/1124)
- Fix broken ReflectionUnionTypes [#1132 / def-studio](https://github.com/barryvdh/laravel-ide-helper/pull/1132)
- Relative class names are not converted to fully-qualified class names [#1005 / SavKS](https://github.com/barryvdh/laravel-ide-helper/pull/1005)
### Fixed
- `Macro::initPhpDoc()` will save original docblock if present [\#1116 / LastDragon-ru](https://github.com/barryvdh/laravel-ide-helper/pull/1116)
- `Alias` will grab macros from `\Illuminate\Database\Eloquent\Builder` too [\#1118 / LastDragon-ru](https://github.com/barryvdh/laravel-ide-helper/pull/1118)
2020-12-08, 2.8.2
-----------------
### Added
- Fix phpdoc generate for custom cast with parameter [\#986 / artelkr](https://github.com/barryvdh/laravel-ide-helper/pull/986)
- Created a possibility to add custom relation type [\#987 / efinder2](https://github.com/barryvdh/laravel-ide-helper/pull/987)
- Added `@see` with macro/mixin definition location to PhpDoc [\#1054 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1054)
- Initial compatibility for PHP8 [\#1106 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1106)
## 2020-12-30, 2.9.0
### Changed
- Implement DeferrableProvider [\#914 / kon-shou](https://github.com/barryvdh/laravel-ide-helper/pull/914)
- Dropped support for Laravel 6 and Laravel 7, as well as support for PHP 7.2 and added support for doctrine/dbal:^3 [#1114 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1114)
### Fixed
- Compatibility with Lumen [\#1043 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1043)
- Allow model_locations to have glob patterns [\#1059 / saackearl](https://github.com/barryvdh/laravel-ide-helper/pull/1059)
- Error when generating helper for macroable classes which are not facades and contain a "fake" method [\#1066 / domkrm] (https://github.com/barryvdh/laravel-ide-helper/pull/1066)
- Casts with a return type of `static` or `$this` now resolve to an instance of the cast [\#1103 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1103)
- `Macro::initPhpDoc()` will save original docblock if present [#1116 / LastDragon-ru](https://github.com/barryvdh/laravel-ide-helper/pull/1116)
- `Alias` will grab macros from `\Illuminate\Database\Eloquent\Builder` too [#1118 / LastDragon-ru](https://github.com/barryvdh/laravel-ide-helper/pull/1118)
## 2020-12-08, 2.8.2
### Added
- Fix phpdoc generate for custom cast with parameter [#986 / artelkr](https://github.com/barryvdh/laravel-ide-helper/pull/986)
- Created a possibility to add custom relation type [#987 / efinder2](https://github.com/barryvdh/laravel-ide-helper/pull/987)
- Added `@see` with macro/mixin definition location to PhpDoc [#1054 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1054)
- Initial compatibility for PHP8 [#1106 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1106)
### Changed
- Implement DeferrableProvider [#914 / kon-shou](https://github.com/barryvdh/laravel-ide-helper/pull/914)
### Fixed
- Compatibility with Lumen [#1043 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1043)
- Allow model_locations to have glob patterns [#1059 / saackearl](https://github.com/barryvdh/laravel-ide-helper/pull/1059)
- Error when generating helper for macroable classes which are not facades and contain a "fake" method [#1066 / domkrm] (https://github.com/barryvdh/laravel-ide-helper/pull/1066)
- Casts with a return type of `static` or `$this` now resolve to an instance of the cast [#1103 / riesjart](https://github.com/barryvdh/laravel-ide-helper/pull/1103)
### Removed
- Removed format and broken generateJsonHelper [\#1053 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1053)
2020-09-07, 2.8.1
-----------------
### Added
- Support Laravel 8 [\#1022 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1022)
- Add option to force usage of FQN [\#1031 / edvordo](https://github.com/barryvdh/laravel-ide-helper/pull/1031)
- Add support for macros of all macroable classes [\#1006 / domkrm](https://github.com/barryvdh/laravel-ide-helper/pull/1006)
- Removed format and broken generateJsonHelper [#1053 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/1053)
## 2020-09-07, 2.8.1
2020-08-11, 2.8.0
-----------------
### Added
- Add static return type to builder methods [\#924 / dmason30](https://github.com/barryvdh/laravel-ide-helper/pull/924)
- Add `optonal` to meta generator for PhpStorm [\#932 / halaei](https://github.com/barryvdh/laravel-ide-helper/pull/932)
- Decimal columns as string in Models [\#948 / fgibaux](https://github.com/barryvdh/laravel-ide-helper/pull/948)
- Simplify full namespaces for already included resources [\#954 / LANGERGabriel](https://github.com/barryvdh/laravel-ide-helper/pull/954)
- Make writing relation count properties optional [\#969 / AegirLeet](https://github.com/barryvdh/laravel-ide-helper/pull/969)
- Add more methods able to resolve container instances [\#996 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/996)
- Support Laravel 8 [#1022 / barryvdh](https://github.com/barryvdh/laravel-ide-helper/pull/1022)
- Add option to force usage of FQN [#1031 / edvordo](https://github.com/barryvdh/laravel-ide-helper/pull/1031)
- Add support for macros of all macroable classes [#1006 / domkrm](https://github.com/barryvdh/laravel-ide-helper/pull/1006)
## 2020-08-11, 2.8.0
### Added
- Add static return type to builder methods [#924 / dmason30](https://github.com/barryvdh/laravel-ide-helper/pull/924)
- Add `optional` to meta generator for PhpStorm [#932 / halaei](https://github.com/barryvdh/laravel-ide-helper/pull/932)
- Decimal columns as string in Models [#948 / fgibaux](https://github.com/barryvdh/laravel-ide-helper/pull/948)
- Simplify full namespaces for already included resources [#954 / LANGERGabriel](https://github.com/barryvdh/laravel-ide-helper/pull/954)
- Make writing relation count properties optional [#969 / AegirLeet](https://github.com/barryvdh/laravel-ide-helper/pull/969)
- Add more methods able to resolve container instances [#996 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/996)
### Fixed
- Test `auth` is bound before detect Auth driver [\#946 / zhwei](https://github.com/barryvdh/laravel-ide-helper/pull/946)
- Fix inline doc-block for final models [\#944 / Gummibeer](https://github.com/barryvdh/laravel-ide-helper/pull/955)
2020-04-22, 2.7.0
-----------------
- Test `auth` is bound before detect Auth driver [#946 / zhwei](https://github.com/barryvdh/laravel-ide-helper/pull/946)
- Fix inline doc-block for final models [#944 / Gummibeer](https://github.com/barryvdh/laravel-ide-helper/pull/955)
## 2020-04-22, 2.7.0
### Added
- Add `ignored_models` as config option [\#890 / pataar](https://github.com/barryvdh/laravel-ide-helper/pull/890)
- Infer return type from reflection if no phpdoc given [\#906 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/906)
- Add custom collection support for get and all methods [\#903 / dmason30](https://github.com/barryvdh/laravel-ide-helper/pull/903)
- if a model implements interfaces, include them in the stub [\#920 / mr-feek](https://github.com/barryvdh/laravel-ide-helper/pull/920)
- Generate noinspections PHPStorm tags [\#905 / mzglinski](https://github.com/barryvdh/laravel-ide-helper/pull/905)
- Added support for Laravel 7 custom casts [\#913 / belamov](https://github.com/barryvdh/laravel-ide-helper/pull/913)
- Ability to use patterns for model_locations [\#921 / 4n70w4](https://github.com/barryvdh/laravel-ide-helper/pull/921)
- Add `ignored_models` as config option [#890 / pataar](https://github.com/barryvdh/laravel-ide-helper/pull/890)
- Infer return type from reflection if no phpdoc given [#906 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/906)
- Add custom collection support for get and all methods [#903 / dmason30](https://github.com/barryvdh/laravel-ide-helper/pull/903)
- if a model implements interfaces, include them in the stub [#920 / mr-feek](https://github.com/barryvdh/laravel-ide-helper/pull/920)
- Generate noinspections PHPStorm tags [#905 / mzglinski](https://github.com/barryvdh/laravel-ide-helper/pull/905)
- Added support for Laravel 7 custom casts [#913 / belamov](https://github.com/barryvdh/laravel-ide-helper/pull/913)
- Ability to use patterns for model_locations [#921 / 4n70w4](https://github.com/barryvdh/laravel-ide-helper/pull/921)
### Fixed
- MorphToMany relations with query not working [\#894 / UksusoFF](https://github.com/barryvdh/laravel-ide-helper/pull/894)
- Fix camelCase duplicated properties generator [\#881 / bop10](https://github.com/barryvdh/laravel-ide-helper/pull/881)
- Prevent generation of invalid code for certain parameter default values [\#901 / loilo](https://github.com/barryvdh/laravel-ide-helper/pull/901)
- Make hasOne and morphOne nullable [\#864 / leo108](https://github.com/barryvdh/laravel-ide-helper/pull/864)
- Remove unnecessary and wrong definition of SoftDelete methods [\#918 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/918)
- Unregister meta command custom autoloader when it is no longer needed [\#919 / mr-feek](https://github.com/barryvdh/laravel-ide-helper/pull/919)
2020-02-25, 2.6.7
-----------------
- MorphToMany relations with query not working [#894 / UksusoFF](https://github.com/barryvdh/laravel-ide-helper/pull/894)
- Fix camelCase duplicated properties generator [#881 / bop10](https://github.com/barryvdh/laravel-ide-helper/pull/881)
- Prevent generation of invalid code for certain parameter default values [#901 / loilo](https://github.com/barryvdh/laravel-ide-helper/pull/901)
- Make hasOne and morphOne nullable [#864 / leo108](https://github.com/barryvdh/laravel-ide-helper/pull/864)
- Remove unnecessary and wrong definition of SoftDelete methods [#918 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/918)
- Unregister meta command custom autoloader when it is no longer needed [#919 / mr-feek](https://github.com/barryvdh/laravel-ide-helper/pull/919)
## 2020-02-25, 2.6.7
### Added
- Support for Laravel 7 [commit by barryvdh](https://github.com/barryvdh/laravel-ide-helper/commit/edd69c5e0508972c81f1f7173236de2459c45814)
2019-12-02, 2.6.6
-----------------
## 2019-12-02, 2.6.6
### Added
- Add splat operator (...) support [\#860 / ngmy](https://github.com/barryvdh/laravel-ide-helper/pull/860)
- Add support for custom date class via Date::use() [\#859 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/859)
- Add splat operator (...) support [#860 / ngmy](https://github.com/barryvdh/laravel-ide-helper/pull/860)
- Add support for custom date class via Date::use() [#859 / mfn](https://github.com/barryvdh/laravel-ide-helper/pull/859)
### Fixed
- Prevent undefined property errors [\#877 / matt-allan](https://github.com/barryvdh/laravel-ide-helper/pull/877)
----
- Prevent undefined property errors [#877 / matt-allan](https://github.com/barryvdh/laravel-ide-helper/pull/877)
---
Missing an older changelog? Feel free to submit a PR!
+27 -42
View File
@@ -11,7 +11,7 @@
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.
The 3.x branch supports Laravel 10 and 11. For older version, use the 2.x releases.
The 3.x branch supports Laravel 10 and later. For older version, use the 2.x releases.
- [Installation](#installation)
- [Usage](#usage)
@@ -33,42 +33,27 @@ Require this package with composer using the following command:
composer require --dev barryvdh/laravel-ide-helper
```
> [!NOTE]
> If you encounter version conflicts with doctrine/dbal, please try:
> `composer require --dev barryvdh/laravel-ide-helper --with-all-dependencies`
This package makes use of [Laravels package auto-discovery mechanism](https://medium.com/@taylorotwell/package-auto-discovery-in-laravel-5-5-ea9e3ab20518), which means if you don't install dev dependencies in production, it also won't be loaded.
If for some reason you want manually control this:
- add the package to the `extra.laravel.dont-discover` key in `composer.json`, e.g.
```json
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-ide-helper"
]
}
}
```
- Add the following class to the `providers` array in `config/app.php`:
```php
Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,
```
If you want to manually load it only in non-production environments, instead you can add this to your `AppServiceProvider` with the `register()` method:
```php
public function register()
{
if ($this->app->isLocal()) {
$this->app->register(\Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class);
}
// ...
}
```
> Note: Avoid caching the configuration in your development environment, it may cause issues after installing this package; respectively clear the cache beforehand via `php artisan cache:clear` if you encounter problems when running the commands
## Usage
### TL;DR
Run this to generate autocompletion for Facades. This creates _ide_helper.php
```
php artisan ide-helper:generate
```
Run this to add phpdocs for your models. Add -RW to Reset existing phpdocs and Write to the models directly.
```
php artisan ide-helper:models -RW
```
If you don't want the full _ide_helper.php file, you can run add `--write-eloquent-helper` to the model command to generate small version, which is required for the `@mixin \Eloquent` to be able to add the QueryBuilder methods.
If you don't want to add all the phpdocs to your Models directly, you can use `--nowrite` to create a separate file. The `--write-mixin` option can be used to only add a `@mixin` to your models, but add the generated phpdocs in a separate file. This avoids having the results marked as duplicate.
_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)
@@ -76,7 +61,7 @@ _Check out [this Laracasts video](https://laracasts.com/series/how-to-be-awesome
- `php artisan ide-helper:meta` - [PhpStorm Meta file](#phpstorm-meta-for-container-instances)
Note: You do need CodeComplice for Sublime Text: https://github.com/spectacles/CodeComplice
> Note: You do need CodeComplice for Sublime Text: https://github.com/spectacles/CodeComplice
### Automatic PHPDoc generation for Laravel Facades
@@ -86,8 +71,6 @@ You can now re-generate the docs yourself (for future updates)
php artisan ide-helper:generate
```
Note: `bootstrap/compiled.php` has to be cleared first, so run `php artisan clear-compiled` before generating.
This will generate the file `_ide_helper.php` which is expected to be additionally parsed by your IDE for autocomplete. You can use the config `filename` to change its name.
You can configure your `composer.json` to do this each time you update your dependencies:
@@ -115,7 +98,7 @@ 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.
> **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.
The `Illuminate/Support/helpers.php` is already set up, but you can add/remove your own files in the config file.
@@ -149,6 +132,8 @@ 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.
> You need the _ide_helper.php file to add the QueryBuilder methods. You can add --write-eloquent-helper/-E to generate a minimal version. If this file does not exist, you will be prompted for it.
Writing to the models should keep the existing comments and only append new properties/methods. It will not update changed properties/methods.
With the `--reset (-R)` option, the whole existing PHPDoc is replaced, including any comments that have been made.
@@ -225,11 +210,11 @@ Eloquent allows calling `where<Attribute>` on your models, e.g. `Post::whereTitl
If for some reason it's undesired to have them generated (one for each column), you can disable this via config `write_model_magic_where` and setting it to `false`.
#### Magic `*_count` properties
#### Magic `*_count` and `*_exists` properties
You may use the [`::withCount`](https://laravel.com/docs/master/eloquent-relationships#counting-related-models) method to count the number results from a relationship without actually loading them. Those results are then placed in attributes following the `<columname>_count` convention.
You may use the [`::withCount`](https://laravel.com/docs/master/eloquent-relationships#counting-related-models) and [`::withExists`](https://laravel.com/docs/master/eloquent-relationships#other-aggregate-functions) methods to count the number results from a relationship without actually loading them. Those results are then placed in attributes following the `<columnname>_count` and `<columnname>_exists` convention.
By default, these attributes are generated in the phpdoc. You can turn them off by setting the config `write_model_relation_count_properties` to `false`.
By default, these attributes are generated in the phpdoc. You can turn them off by setting the config `write_model_relation_count_properties` and `write_model_relation_exists_properties` to `false`.
#### Generics annotations
@@ -382,7 +367,7 @@ app(App\SomeClass::class);
```
> Note: You might need to restart PhpStorm and make sure `.phpstorm.meta.php` is indexed.
>
> Note: When you receive a FatalException: class not found, check your config
> (for example, remove S3 as cloud driver when you don't have S3 configured. Remove Redis ServiceProvider when you don't use it).
+18 -17
View File
@@ -11,7 +11,8 @@
"netbeans",
"sublime",
"codeintel",
"phpdoc"
"phpdoc",
"dev"
],
"authors": [
{
@@ -22,25 +23,25 @@
"require": {
"php": "^8.2",
"ext-json": "*",
"barryvdh/reflection-docblock": "^2.1.2",
"barryvdh/reflection-docblock": "^2.4",
"composer/class-map-generator": "^1.0",
"illuminate/console": "^11.15",
"illuminate/database": "^11.15",
"illuminate/filesystem": "^11.15",
"illuminate/support": "^11.15",
"nikic/php-parser": "^4.18 || ^5",
"phpdocumentor/type-resolver": "^1.1.0"
"illuminate/console": "^11.15 || ^12 || ^13.0",
"illuminate/database": "^11.15 || ^12 || ^13.0",
"illuminate/filesystem": "^11.15 || ^12 || ^13.0",
"illuminate/support": "^11.15 || ^12 || ^13.0"
},
"require-dev": {
"ext-pdo_sqlite": "*",
"friendsofphp/php-cs-fixer": "^3",
"illuminate/config": "^11.15",
"illuminate/view": "^11.15",
"illuminate/config": "^11.15 || ^12 || ^13.0",
"illuminate/view": "^11.15 || ^12 || ^13.0",
"larastan/larastan": "^3.1",
"mockery/mockery": "^1.4",
"orchestra/testbench": "^9.2",
"phpunit/phpunit": "^10.5",
"orchestra/testbench": "^9.2 || ^10 || ^11.0",
"phpstan/phpstan-phpunit": "^2.0",
"phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.12",
"spatie/phpunit-snapshot-assertions": "^4 || ^5",
"vimeo/psalm": "^5.4"
"vlucas/phpdotenv": "^5"
},
"suggest": {
"illuminate/events": "Required for automatic helper generation (^6|^7|^8|^9|^10|^11)."
@@ -65,7 +66,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "3.2-dev"
"dev-master": "3.7-dev"
},
"laravel": {
"providers": [
@@ -74,7 +75,8 @@
}
},
"scripts": {
"analyze": "psalm",
"analyze": "phpstan",
"analyze-set-baseline": "phpstan --generate-baseline",
"check-style": [
"php-cs-fixer fix --diff --diff-format=udiff --dry-run",
"php-cs-fixer fix --diff --diff-format=udiff --dry-run --config=.php_cs.tests.php"
@@ -83,9 +85,8 @@
"php-cs-fixer fix",
"php-cs-fixer fix --config=.php-cs-fixer.tests.php"
],
"psalm-set-baseline": "psalm --set-baseline=psalm-baseline.xml",
"test": "phpunit",
"test-ci": "phpunit -d --without-creating-snapshots",
"test-ci": "phpunit",
"test-regenerate": "phpunit -d --update-snapshots"
}
}
+91 -31
View File
@@ -49,17 +49,14 @@ return [
/*
|--------------------------------------------------------------------------
| Factory builders
| Write model query methods
|--------------------------------------------------------------------------
|
| Set to true to generate factory generators for better factory()
| method auto-completion.
|
| Deprecated for Laravel 8 or latest.
| Set to false to disable generated docs for the 'query()', 'newQuery()' and 'newModelQuery()' methods.
|
*/
'include_factory_builders' => false,
'write_query_methods' => true,
/*
|--------------------------------------------------------------------------
@@ -85,14 +82,16 @@ return [
/*
|--------------------------------------------------------------------------
| Write model relation count properties
| Write model relation count and exists properties
|--------------------------------------------------------------------------
|
| Set to false to disable writing of relation count properties to model DocBlocks.
| Set to false to disable writing of relation count and exists properties
| to model DocBlocks.
|
*/
'write_model_relation_count_properties' => true,
'write_model_relation_exists_properties' => false,
/*
|--------------------------------------------------------------------------
@@ -123,6 +122,7 @@ return [
'helper_files' => [
base_path() . '/vendor/laravel/framework/src/Illuminate/Support/helpers.php',
base_path() . '/vendor/laravel/framework/src/Illuminate/Foundation/helpers.php',
],
/*
@@ -201,29 +201,29 @@ return [
],
/*
|--------------------------------------------------------------------------
| Support for camel cased models
|--------------------------------------------------------------------------
|
| There are some Laravel packages (such as Eloquence) that allow for accessing
| Eloquent model properties via camel case, instead of snake case.
|
| Enabling this option will support these packages by saving all model
| properties as camel case, instead of snake case.
|
| For example, normally you would see this:
|
| * @property \Illuminate\Support\Carbon $created_at
| * @property \Illuminate\Support\Carbon $updated_at
|
| With this enabled, the properties will be this:
|
| * @property \Illuminate\Support\Carbon $createdAt
| * @property \Illuminate\Support\Carbon $updatedAt
|
| Note, it is currently an all-or-nothing option.
|
*/
|--------------------------------------------------------------------------
| Support for camel cased models
|--------------------------------------------------------------------------
|
| There are some Laravel packages (such as Eloquence) that allow for accessing
| Eloquent model properties via camel case, instead of snake case.
|
| Enabling this option will support these packages by saving all model
| properties as camel case, instead of snake case.
|
| For example, normally you would see this:
|
| * @property \Illuminate\Support\Carbon $created_at
| * @property \Illuminate\Support\Carbon $updated_at
|
| With this enabled, the properties will be this:
|
| * @property \Illuminate\Support\Carbon $createdAt
| * @property \Illuminate\Support\Carbon $updatedAt
|
| Note, it is currently an all-or-nothing option.
|
*/
'model_camel_case_properties' => false,
/*
@@ -273,6 +273,20 @@ return [
*/
'use_generics_annotations' => true,
/*
|--------------------------------------------------------------------------
| Default return types for macros
|--------------------------------------------------------------------------
|
| Define default return types for macros without explicit return types.
| e.g. `\Illuminate\Database\Query\Builder::class => 'static'`,
| `\Illuminate\Support\Str::class => 'string'`
|
*/
'macro_default_return_types' => [
Illuminate\Http\Client\Factory::class => Illuminate\Http\Client\PendingRequest::class,
],
/*
|--------------------------------------------------------------------------
| Additional relation types
@@ -299,6 +313,52 @@ return [
*/
'additional_relation_return_types' => [],
/*
|--------------------------------------------------------------------------
| Enforce nullable Eloquent relationships on not null columns
|--------------------------------------------------------------------------
|
| When set to true (default), this option enforces nullable Eloquent relationships.
| However, in cases where the application logic ensures the presence of related
| records it may be desirable to set this option to false to avoid unwanted null warnings.
|
| Default: true
| A not null column with no foreign key constraint will have a "nullable" relationship.
| * @property int $not_null_column_with_no_foreign_key_constraint
| * @property-read BelongsToVariation|null $notNullColumnWithNoForeignKeyConstraint
|
| Option: false
| A not null column with no foreign key constraint will have a "not nullable" relationship.
| * @property int $not_null_column_with_no_foreign_key_constraint
| * @property-read BelongsToVariation $notNullColumnWithNoForeignKeyConstraint
|
*/
'enforce_nullable_relationships' => true,
/*
|--------------------------------------------------------------------------
| Make soft deletable relations nullable
|--------------------------------------------------------------------------
|
| When set to true (default), relationships to models using SoftDeletes trait
| will be marked as nullable. This is because soft-deleted records are excluded
| from queries by default, meaning even non-nullable foreign keys can return
| null when the related model is soft-deleted.
|
| Default: true
| A relationship to a soft-deletable model will include |null in the type:
| * @property-read Team|null $team
|
| Option: false
| A relationship to a soft-deletable model will NOT include |null (unless
| nullable for other reasons such as nullable foreign key column):
| * @property-read Team $team
|
*/
'soft_deletes_force_nullable' => true,
/*
|--------------------------------------------------------------------------
| Run artisan commands after migrations to generate model helpers
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Taylor Otwell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+5
View File
@@ -0,0 +1,5 @@
The templates here are based on the official VS Code extension by Laravel https://github.com/laravel/vs-code-extension
Modifications:
- return instead of echo
- do not serialize to JSON
+43
View File
@@ -0,0 +1,43 @@
<?php
return collect(Illuminate\Support\Facades\Gate::abilities())
->map(function ($policy, $key) {
$reflection = new ReflectionFunction($policy);
$policyClass = null;
$closureThis = $reflection->getClosureThis();
if ($closureThis && get_class($closureThis) === Illuminate\Auth\Access\Gate::class) {
$vars = $reflection->getClosureUsedVariables();
if (isset($vars['callback'])) {
[$policyClass, $method] = explode('@', $vars['callback']);
$reflection = new ReflectionMethod($policyClass, $method);
}
}
return [
'key' => $key,
'uri' => $reflection->getFileName(),
'policy_class' => $policyClass,
'lineNumber' => $reflection->getStartLine(),
];
})
->merge(
collect(Illuminate\Support\Facades\Gate::policies())->flatMap(function ($policy, $model) {
$methods = (new ReflectionClass($policy))->getMethods();
return collect($methods)->map(function (ReflectionMethod $method) use ($policy) {
return [
'key' => $method->getName(),
'uri' => $method->getFileName(),
'policy_class' => $policy,
'lineNumber' => $method->getStartLine(),
];
})->filter(function ($ability) {
return !in_array($ability['key'], ['allow', 'deny']);
});
}),
)
->values()
->groupBy('key');
+131
View File
@@ -0,0 +1,131 @@
<?php
$local = collect(glob(config_path('/*.php')))
->merge(glob(config_path('**/*.php')))
->map(fn ($path) => [
(string) Illuminate\Support\Str::of($path)
->replace([config_path('/'), '.php'], '')
->replace('/', '.'),
$path,
]);
$vendor = collect(glob(base_path('vendor/**/**/config/*.php')))->map(fn (
$path
) => [
(string) Illuminate\Support\Str::of($path)
->afterLast('/config/')
->replace('.php', '')
->replace('/', '.'),
$path,
]);
$configPaths = $local
->merge($vendor)
->groupBy(0)
->map(fn ($items) => $items->pluck(1));
$cachedContents = [];
$cachedParsed = [];
function vsCodeGetConfigValue($value, $key, $configPaths)
{
$parts = explode('.', $key);
$toFind = $key;
$found = null;
while (count($parts) > 0) {
array_pop($parts);
$toFind = implode('.', $parts);
if ($configPaths->has($toFind)) {
$found = $toFind;
break;
}
}
if ($found === null) {
return null;
}
$file = null;
$line = null;
if ($found === $key) {
$file = $configPaths->get($found)[0];
} else {
foreach ($configPaths->get($found) as $path) {
$cachedContents[$path] ??= file_get_contents($path);
$cachedParsed[$path] ??= token_get_all($cachedContents[$path]);
$keysToFind = Illuminate\Support\Str::of($key)
->replaceFirst($found, '')
->ltrim('.')
->explode('.');
if (is_numeric($keysToFind->last())) {
$index = $keysToFind->pop();
if ($index !== '0') {
return null;
}
$key = collect(explode('.', $key));
$key->pop();
$key = $key->implode('.');
$value = [];
}
$nextKey = $keysToFind->shift();
$expectedDepth = 1;
$depth = 0;
foreach ($cachedParsed[$path] as $token) {
if ($token === '[') {
$depth++;
}
if ($token === ']') {
$depth--;
}
if (!is_array($token)) {
continue;
}
$str = trim($token[1], '"\'');
if (
$str === $nextKey &&
$depth === $expectedDepth &&
$token[0] === T_CONSTANT_ENCAPSED_STRING
) {
$nextKey = $keysToFind->shift();
$expectedDepth++;
if ($nextKey === null) {
$file = $path;
$line = $token[2];
break;
}
}
}
if ($file) {
break;
}
}
}
return [
'name' => $key,
'value' => $value,
'file' => $file === null ? null : str_replace(base_path('/'), '', $file),
'line' => $line,
];
}
return collect(Illuminate\Support\Arr::dot(config()->all()))
->map(fn ($value, $key) => vsCodeGetConfigValue($value, $key, $configPaths))
->filter()
->values();
+64
View File
@@ -0,0 +1,64 @@
<?php
return collect(app("Illuminate\Contracts\Http\Kernel")->getMiddlewareGroups())
->merge(app("Illuminate\Contracts\Http\Kernel")->getRouteMiddleware())
->map(function ($middleware, $key) {
$result = [
'class' => null,
'uri' => null,
'startLine' => null,
'parameters' => null,
'groups' => [],
];
if (is_array($middleware)) {
$result['groups'] = collect($middleware)->map(function ($m) {
if (!class_exists($m)) {
return [
'class' => $m,
'uri' => null,
'startLine' => null,
];
}
$reflected = new ReflectionClass($m);
$reflectedMethod = $reflected->getMethod('handle');
return [
'class' => $m,
'uri' => $reflected->getFileName(),
'startLine' =>
$reflectedMethod->getFileName() === $reflected->getFileName()
? $reflectedMethod->getStartLine()
: null,
];
})->all();
return $result;
}
$reflected = new ReflectionClass($middleware);
$reflectedMethod = $reflected->getMethod('handle');
$result = array_merge($result, [
'class' => $middleware,
'uri' => $reflected->getFileName(),
'startLine' => $reflectedMethod->getStartLine(),
]);
$parameters = collect($reflectedMethod->getParameters())
->filter(function ($rc) {
return $rc->getName() !== 'request' && $rc->getName() !== 'next';
})
->map(function ($rc) {
return $rc->getName() . ($rc->isVariadic() ? '...' : '');
});
if ($parameters->isEmpty()) {
return $result;
}
return array_merge($result, [
'parameters' => $parameters->implode(','),
]);
});
+46
View File
@@ -0,0 +1,46 @@
<?php
function vsCodeGetRouterReflection(Illuminate\Routing\Route $route)
{
if ($route->getActionName() === 'Closure') {
return new ReflectionFunction($route->getAction()['uses']);
}
if (!str_contains($route->getActionName(), '@')) {
return new ReflectionClass($route->getActionName());
}
try {
return new ReflectionMethod($route->getControllerClass(), $route->getActionMethod());
} catch (Throwable $e) {
$namespace = app(Illuminate\Routing\UrlGenerator::class)->getRootControllerNamespace()
?? (app()->getNamespace() . 'Http\Controllers');
return new ReflectionMethod(
$namespace . '\\' . ltrim($route->getControllerClass(), '\\'),
$route->getActionMethod(),
);
}
}
return collect(app('router')->getRoutes()->getRoutes())
->map(function (Illuminate\Routing\Route $route) {
try {
$reflection = vsCodeGetRouterReflection($route);
} catch (Throwable $e) {
$reflection = null;
}
return [
'method' => collect($route->methods())->filter(function ($method) {
return $method !== 'HEAD';
})->implode('|'),
'uri' => $route->uri(),
'name' => $route->getName(),
'action' => $route->getActionName(),
'parameters' => $route->parameterNames(),
'filename' => $reflection ? $reflection->getFileName() : null,
'line' => $reflection ? $reflection->getStartLine() : null,
];
})
;
+160
View File
@@ -0,0 +1,160 @@
<?php
function vsCodeGetTranslationsFromFile(Symfony\Component\Finder\SplFileInfo $file, $path, $namespace)
{
if ($file->getExtension() !== 'php') {
return null;
}
$filePath = $file->getRealPath();
$relativePath = trim(str_replace($path, '', $file->getPath()), DIRECTORY_SEPARATOR);
$lang = explode(DIRECTORY_SEPARATOR, $relativePath)[0] ?? null;
if (!$lang) {
return null;
}
$keyPath = str_replace($path . DIRECTORY_SEPARATOR . $lang . DIRECTORY_SEPARATOR, '', $filePath);
$keyWithSlashes = str_replace('.php', '', $keyPath);
$baseKey = str_replace(DIRECTORY_SEPARATOR, '.', $keyWithSlashes);
if ($namespace) {
$baseKey = "{$namespace}::{$baseKey}";
}
try {
$translations = require $filePath;
} catch (Throwable $e) {
return null;
}
if (!is_array($translations)) {
return null;
}
$fileLines = Illuminate\Support\Facades\File::lines($filePath);
$lines = [];
$inComment = false;
foreach ($fileLines as $index => $line) {
$trimmed = trim($line);
if (str_starts_with($trimmed, '/*')) {
$inComment = true;
}
if ($inComment) {
if (str_ends_with($trimmed, '*/')) {
$inComment = false;
}
continue;
}
if (str_starts_with($trimmed, '//')) {
continue;
}
$lines[] = [$index + 1, $trimmed];
}
return [
'k' => $baseKey,
'la' => $lang,
'vs' => collect(Illuminate\Support\Arr::dot($translations))
->map(
fn ($value, $dotKey) => vsCodeTranslationValue(
$dotKey,
$value,
str_replace(base_path(DIRECTORY_SEPARATOR), '', $filePath),
$lines
)
)
->filter(),
];
}
function vsCodeTranslationValue($key, $value, $file, $lines): ?array
{
if (is_array($value)) {
return null;
}
$lineNumber = 1;
$keys = explode('.', $key);
$currentKey = array_shift($keys);
foreach ($lines as $line) {
if (
strpos($line[1], '"' . $currentKey . '"') !== false ||
strpos($line[1], "'" . $currentKey . "'") !== false
) {
$lineNumber = $line[0];
$currentKey = array_shift($keys);
}
if ($currentKey === null) {
break;
}
}
return [
'v' => $value,
'p' => $file,
'li' => $lineNumber,
'pa' => preg_match_all("/\:([A-Za-z0-9_]+)/", $value, $matches)
? $matches[1]
: [],
];
}
function vscodeCollectTranslations(string $path, ?string $namespace = null)
{
$realPath = realpath($path);
if (!is_dir($realPath)) {
return collect();
}
return collect(Illuminate\Support\Facades\File::allFiles($realPath))
->map(fn ($file) => vsCodeGetTranslationsFromFile($file, $path, $namespace))
->filter();
}
$loader = app('translator')->getLoader();
$namespaces = $loader->namespaces();
$reflection = new ReflectionClass($loader);
$property = $reflection->hasProperty('paths')
? $reflection->getProperty('paths')
: $reflection->getProperty('path');
$paths = Illuminate\Support\Arr::wrap($property->getValue($loader));
$default = collect($paths)->flatMap(
fn ($path) => vscodeCollectTranslations($path)
);
$namespaced = collect($namespaces)->flatMap(
fn ($path, $namespace) => vscodeCollectTranslations($path, $namespace)
);
$final = [];
foreach ($default->merge($namespaced) as $value) {
if (!isset($value['vs']) || !is_iterable($value['vs'])) {
continue;
}
foreach ($value['vs'] as $key => $v) {
$dotKey = "{$value['k']}.{$key}";
if (!isset($final[$dotKey])) {
$final[$dotKey] = [];
}
$final[$dotKey][$value['la']] = $v;
if ($value['la'] === Illuminate\Support\Facades\App::currentLocale()) {
$final[$dotKey]['default'] = $v;
}
}
}
return collect($final);
+61
View File
@@ -0,0 +1,61 @@
<?php
function vsCodeFindBladeFiles($path)
{
$paths = [];
if (!is_dir($path)) {
return $paths;
}
foreach (
Symfony\Component\Finder\Finder::create()
->files()
->name('*.blade.php')
->in($path) as $file
) {
$paths[] = [
'path' => str_replace(base_path(DIRECTORY_SEPARATOR), '', $file->getRealPath()),
'isVendor' => str_contains($file->getRealPath(), base_path('vendor')),
'key' => Illuminate\Support\Str::of($file->getRealPath())
->replace(realpath($path), '')
->replace('.blade.php', '')
->ltrim(DIRECTORY_SEPARATOR)
->replace(DIRECTORY_SEPARATOR, '.'),
];
}
return $paths;
}
$paths = collect(
app('view')
->getFinder()
->getPaths()
)->flatMap(function ($path) {
return vsCodeFindBladeFiles($path);
});
$hints = collect(
app('view')
->getFinder()
->getHints()
)->flatMap(function ($paths, $key) {
return collect($paths)->flatMap(function ($path) use ($key) {
return collect(vsCodeFindBladeFiles($path))->map(function ($value) use (
$key
) {
return array_merge($value, ['key' => "{$key}::{$value['key']}"]);
});
});
});
[$local, $vendor] = $paths
->merge($hints)
->values()
->partition(function ($v) {
return !$v['isVendor'];
});
return $local
->sortBy('key', SORT_NATURAL)
->merge($vendor->sortBy('key', SORT_NATURAL));
+67
View File
@@ -0,0 +1,67 @@
parameters:
ignoreErrors:
-
message: '#^File ends with a trailing whitespace\. This may cause problems when running the code in the web browser\. Remove the closing \?\> mark or remove the whitespace\.$#'
identifier: whitespace.fileEnd
count: 1
path: resources/views/helper.php
-
message: '#^Call to an undefined method Illuminate\\Contracts\\Filesystem\\Filesystem\:\:requireOnce\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Console/MetaCommand.php
-
message: '#^Call to an undefined method Illuminate\\Contracts\\Foundation\\Application\:\:getBindings\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Console/MetaCommand.php
-
message: '#^Call to an undefined method Barryvdh\\Reflection\\DocBlock\\Tag\:\:getMethodName\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Console/ModelsCommand.php
-
message: '#^Call to an undefined method Barryvdh\\Reflection\\DocBlock\\Tag\:\:getType\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Console/ModelsCommand.php
-
message: '#^Call to an undefined method Barryvdh\\Reflection\\DocBlock\\Tag\:\:getVariableName\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Console/ModelsCommand.php
-
message: '#^Call to an undefined method Illuminate\\Support\\Optional\:\:getNumberOfParameters\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Console/ModelsCommand.php
-
message: '#^Call to an undefined method Illuminate\\Support\\Optional\:\:getParameters\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Console/ModelsCommand.php
-
message: '#^Call to an undefined method Illuminate\\Support\\Optional\:\:getReturnType\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Console/ModelsCommand.php
-
message: '#^Call to an undefined static method Illuminate\\Database\\Eloquent\\Model\:\:newFactory\(\)\.$#'
identifier: staticMethod.notFound
count: 2
path: src/Console/ModelsCommand.php
-
message: '#^Call to an undefined method ReflectionFunctionAbstract\:\:getDeclaringClass\(\)\.$#'
identifier: method.notFound
count: 1
path: src/Method.php
+11
View File
@@ -0,0 +1,11 @@
includes:
- phpstan-baseline.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
parameters:
level: 2
paths:
- src
- resources/views
excludePaths:
-24
View File
@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="5.4.0@62db5d4f6a7ae0a20f7cc5a4952d730272fc0863">
<file src="src/Factories.php">
<UndefinedClass occurrences="2">
<code>Factory</code>
<code>Factory</code>
</UndefinedClass>
</file>
<file src="src/Generator.php">
<UndefinedClass occurrences="6">
<code>\Auth</code>
<code>\Cache</code>
<code>\Cache</code>
<code>\DB</code>
<code>\Queue</code>
<code>\Storage</code>
</UndefinedClass>
</file>
<file src="src/Console/ModelsCommand.php">
<UndefinedClass occurrences="1">
<code>\UnitEnum</code>
</UndefinedClass>
</file>
</files>
-16
View File
@@ -1,16 +0,0 @@
<?xml version="1.0"?>
<psalm
errorLevel="7"
resolveFromConfigFile="true"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
errorBaseline="psalm-baseline.xml"
>
<projectFiles>
<directory name="src" />
<ignoreFiles>
<directory name="vendor" />
</ignoreFiles>
</projectFiles>
</psalm>
+41 -42
View File
@@ -1,8 +1,9 @@
<?= '<?php' ?>
<?php
/**
* @var \Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_alias_ns
* @var \Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_extends_ns
* @var Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_alias_ns
* @var Barryvdh\LaravelIdeHelper\Alias[][] $namespaces_by_extends_ns
* @var string[] $real_time_facades
* @var bool $include_fluent
* @var string $helpers
*/
@@ -14,31 +15,34 @@
/**
* A helper file for Laravel, to provide autocomplete information to your IDE
* Generated for Laravel <?= $version ?>.
* Generated for Laravel <?= app()->version() ?>.
*
* This file should not be included in your code, only analyzed by your IDE!
*
* @author Barry vd. Heuvel <[email protected]>
* @see https://github.com/barryvdh/laravel-ide-helper
*/
<?php
$s1 = ' ';
$s2 = $s1 . $s1;
$s3 = $s1 . $s2;
?>
<?php foreach ($namespaces_by_extends_ns as $namespace => $aliases) : ?>
namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
namespace <?= $namespace === '__root' ? '' : trim($namespace, '\\') ?> {
<?php foreach ($aliases as $alias) : ?>
<?= trim($alias->getDocComment(' ')) ?>
<?= $alias->getClassType() ?> <?= $alias->getExtendsClass() ?> {
<?php echo trim($alias->getDocComment($s1)) . "\n{$s1}" . $alias->getClassType() ?> <?= $alias->getExtendsClass() ?><?php if ($alias->shouldExtendParentClass()): ?> extends <?= $alias->getParentClass() ?><?php endif; ?> {
<?php foreach ($alias->getMethods() as $method) : ?>
<?= trim($method->getDocComment(' ')) ?>
public static function <?= $method->getName() ?>(<?= $method->getParamsWithDefault() ?>)
<?= trim($method->getDocComment($s2)) . "\n{$s2}" ?>public static function <?= $method->getName() ?>(<?= $method->getParamsWithDefault() ?>)
{<?php if ($method->getDeclaringClass() !== $method->getRoot()) : ?>
//Method inherited from <?= $method->getDeclaringClass() ?>
<?php endif; ?>
<?= "\n" . $s3?>//Method inherited from <?= $method->getDeclaringClass() ?>
<?php endif; ?>
<?php if ($method->isInstanceCall()) : ?>
/** @var <?=$method->getRoot()?> $instance */
<?php endif?>
<?= $method->shouldReturn() ? 'return ' : '' ?><?= $method->getRootMethodCall() ?>;
<?php if ($method->isInstanceCall()) : ?>
<?= $s3 ?>/** @var <?=$method->getRoot()?> $instance */
<?php endif?>
<?= $s3 . ($method->shouldReturn() ? 'return ' : '') ?><?= $method->getRootMethodCall() ?>;
}
<?php endforeach; ?>
}
<?php endforeach; ?>
@@ -47,29 +51,33 @@ namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
<?php endforeach; ?>
<?php foreach ($namespaces_by_alias_ns as $namespace => $aliases) : ?>
namespace <?= $namespace == '__root' ? '' : trim($namespace, '\\') ?> {
<?php foreach ($aliases as $alias) : ?>
<?= $alias->getClassType() ?> <?= $alias->getShortName() ?> extends <?= $alias->getExtends() ?> {<?php if ($alias->getExtendsNamespace() == '\Illuminate\Database\Eloquent') : ?>
<?php foreach ($alias->getMethods() as $method) : ?>
<?= trim($method->getDocComment(' ')) ?>
public static function <?= $method->getName() ?>(<?= $method->getParamsWithDefault() ?>)
{<?php if ($method->getDeclaringClass() !== $method->getRoot()) : ?>
//Method inherited from <?= $method->getDeclaringClass() ?>
<?php endif; ?>
namespace <?= $namespace === '__root' ? '' : trim($namespace, '\\') ?> {
<?php foreach ($aliases as $alias) : ?>
<?php if ($alias->getExtendsNamespace() === '\Illuminate\Database\Eloquent') : ?>
<?= "\n" . $alias->getPhpDocTemplates($s1) . "\n" ?>
<?php endif?>
<?= $s1 . $alias->getClassType() ?> <?= $alias->getShortName() ?> extends <?= $alias->getExtends() ?> {<?php if ($alias->getExtendsNamespace() === '\Illuminate\Database\Eloquent') : ?>
<?php foreach ($alias->getMethods() as $method) : ?>
<?= $s2 . trim($method->getDocComment($s2)) . "\n" ?>
<?= $s2 ?>public static function <?= $method->getName() ?>(<?= $method->getParamsWithDefault() ?>)
<?= $s2?>{<?php if ($method->getDeclaringClass() !== $method->getRoot()) : ?>
<?= $s2 ?>//Method inherited from <?= $method->getDeclaringClass() ?>
<?php endif; ?>
<?php if ($method->isInstanceCall()) : ?>
/** @var <?=$method->getRoot()?> $instance */
<?php endif?>
<?= $method->shouldReturn() ? 'return ' : '' ?><?= $method->getRootMethodCall() ?>;
}
<?php endforeach; ?>
<?php endif; ?>}
<?php endforeach; ?>
<?php if ($method->isInstanceCall()) : ?>
<?= $s3 ?>/** @var <?=$method->getRoot()?> $instance */
<?php endif?>
<?= $s3 . ($method->shouldReturn() ? 'return ' : '') ?><?= $method->getRootMethodCall() ?>;
<?= $s2 ?>}
<?php endforeach; ?>
<?php endif; ?>}
<?php endforeach; ?>
}
<?php endforeach; ?>
<?php foreach($real_time_facades as $name): ?>
<?php foreach ($real_time_facades as $name): ?>
<?php $nested = explode('\\', str_replace('\\' . class_basename($name), '', $name)); ?>
namespace <?php echo implode('\\', $nested); ?> {
/**
@@ -112,12 +120,3 @@ namespace Illuminate\Support {
}
<?php endif ?>
<?php foreach ($factories as $factory) : ?>
namespace <?=$factory->getNamespaceName()?> {
/**
* @method \Illuminate\Database\Eloquent\Collection|<?=$factory->getShortName()?>[]|<?=$factory->getShortName()?> create($attributes = [])
* @method \Illuminate\Database\Eloquent\Collection|<?=$factory->getShortName()?>[]|<?=$factory->getShortName()?> make($attributes = [])
*/
class <?=$factory->getShortName()?>FactoryBuilder extends \Illuminate\Database\Eloquent\FactoryBuilder {}
}
<?php endforeach; ?>
+50 -6
View File
@@ -1,4 +1,16 @@
<?= '<?php' ?>
<?php
/**
* @var array $bindings
* @var string[] $methods
* @var string[] $configMethods
* @var Illuminate\Support\Collection $configValues
* @var array<string, array> $expectedArgumentSets
* @var array $expectedArguments
* @var string[] $userMethods
* @var string $userModel/
* */
?>
/* @noinspection ALL */
// @formatter:off
@@ -21,14 +33,19 @@ namespace PHPSTORM_META {
]));
<?php endforeach; ?>
<?php if (count($factories)) : ?>
override(\factory(0), map([
'' => '@FactoryBuilder',
<?php foreach ($factories as $factory) : ?>
'<?= $factory->getName() ?>' => \<?= $factory->getName() ?>FactoryBuilder::class,
<?php foreach ($userMethods as $method) : ?>
override(<?= $method ?>, map([
'' => \<?= $userModel ?>::class,
]));
<?php endforeach; ?>
<?php foreach ($configMethods as $method) : ?>
override(<?= $method ?>, map([
<?php foreach ($configValues as $name => $value) : ?>
'<?= $name ?>' => '<?= $value ?>',
<?php endforeach; ?>
]));
<?php endif; ?>
<?php endforeach; ?>
override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::mock(0), map(["" => "@&\Mockery\MockInterface"]));
override(\Illuminate\Foundation\Testing\Concerns\InteractsWithContainer::partialMock(0), map(["" => "@&\Mockery\MockInterface"]));
@@ -65,4 +82,31 @@ namespace PHPSTORM_META {
override(\tap(0), type(0));
override(\optional(0), type(0));
<?php if ($expectedArgumentSets): ?>
<?php foreach ($expectedArgumentSets as $name => $argumentsList) : ?>
registerArgumentsSet('<?= $name ?>', <?php foreach ($argumentsList as $i => $arg) : ?><?php if ($i % 5 == 0) {
echo "\n";
} ?><?= var_export($arg, true); ?>,<?php endforeach; ?>);
<?php endforeach; ?>
<?php endif ?>
<?php if ($expectedArguments) : ?>
<?php foreach ($expectedArguments as $arguments) : ?>
<?php
$classes = isset($arguments['class']) ? (array) $arguments['class'] : [null];
$index = $arguments['index'] ?? 0;
$argumentSet = $arguments['argumentSet'];
$functions = [];
foreach ($classes as $class) {
foreach ((array) $arguments['method'] as $method) {
$functions[] = '\\' . ($class ? ltrim($class, '\\') . '::' : '') . $method . '()';
}
}
?>
<?php foreach ($functions as $function) : ?>
expectedArguments(<?= $function ?>, <?= $index ?>, argumentsSet('<?= $argumentSet ?>'));
<?php endforeach; ?>
<?php endforeach; ?>
<?php endif; ?>
}
+168 -23
View File
@@ -13,12 +13,16 @@ namespace Barryvdh\LaravelIdeHelper;
use Barryvdh\Reflection\DocBlock;
use Barryvdh\Reflection\DocBlock\Context;
use Barryvdh\Reflection\DocBlock\ContextFactory;
use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
use Barryvdh\Reflection\DocBlock\Tag\MethodTag;
use Barryvdh\Reflection\DocBlock\Tag\TemplateTag;
use Closure;
use Illuminate\Config\Repository as ConfigRepository;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Traits\Macroable;
use ReflectionClass;
use Throwable;
@@ -33,6 +37,7 @@ class Alias
protected $classType = 'class';
protected $short;
protected $namespace = '__root';
protected $parentClass;
protected $root = null;
protected $classes = [];
protected $methods = [];
@@ -46,6 +51,9 @@ class Alias
/** @var ConfigRepository */
protected $config;
/** @var string[] */
protected $templateNames;
/**
* @param ConfigRepository $config
* @param string $alias
@@ -78,16 +86,27 @@ class Alias
$this->detectNamespace();
$this->detectClassType();
$this->detectExtendsNamespace();
$this->detectParentClass();
if (!empty($this->namespace)) {
$this->classAliases = (new UsesResolver())->loadFromClass($this->root);
try {
$this->classAliases = (new ContextFactory())->createFromReflector(new ReflectionClass($this->root))->getNamespaceAliases();
} catch (Throwable $e) {
$this->classAliases = [];
}
//Create a DocBlock and serializer instance
$this->phpdoc = new DocBlock(new ReflectionClass($alias), new Context($this->namespace, $this->classAliases));
}
if ($facade === '\Illuminate\Database\Eloquent\Model') {
$this->usedMethods = ['decrement', 'increment'];
$this->usedMethods = [
'decrement' => true,
'decrementEach' => true,
'increment' => true,
'incrementEach' => true,
];
}
}
@@ -157,6 +176,25 @@ class Alias
return $this->extendsNamespace;
}
/**
* Get the parent class of the class which this alias extends
*
* @return null|string
*/
public function getParentClass()
{
return $this->parentClass;
}
/**
* Check if this class should extend the parent class
*/
public function shouldExtendParentClass()
{
return $this->parentClass
&& !is_subclass_of($this->extends, Facade::class);
}
/**
* Get the Alias by which this class is called
*
@@ -215,11 +253,17 @@ class Alias
return;
}
$reflection = new \ReflectionMethod($facade, 'fake');
if ($reflection->getNumberOfRequiredParameters() > 0) {
return;
}
$real = $facade::getFacadeRoot();
try {
$facade::fake();
$fake = $facade::getFacadeRoot();
if ($fake !== $real) {
$this->addClass(get_class($fake));
}
@@ -254,6 +298,18 @@ class Alias
}
}
/**
* Detect the parent class
*/
protected function detectParentClass()
{
$reflection = new ReflectionClass($this->root);
$parentClass = $reflection->getParentClass();
$this->parentClass = $parentClass ? '\\' . $parentClass->getName() : null;
}
/**
* Detect the class type
*/
@@ -274,9 +330,8 @@ class Alias
/**
* Get the real root of a facade
*
* @return bool|string
*/
protected function detectRoot()
protected function detectRoot(): void
{
$facade = $this->facade;
@@ -331,11 +386,19 @@ class Alias
$method = new \ReflectionMethod($className, $name);
$class = new ReflectionClass($className);
if (!in_array($magic, $this->usedMethods)) {
if (!isset($this->usedMethods[$magic])) {
if ($class !== $this->root) {
$this->methods[] = new Method($method, $this->alias, $class, $magic, $this->interfaces, $this->classAliases);
$this->methods[] = new Method(
$method,
$class,
$magic,
$this->interfaces,
$this->classAliases,
$this->getReturnTypeNormalizers($class),
$this->getTemplateNames()
);
}
$this->usedMethods[] = $magic;
$this->usedMethods[$magic] = true;
}
}
}
@@ -343,7 +406,6 @@ class Alias
/**
* Get the methods for one or multiple classes.
*
* @return string
*/
protected function detectMethods()
{
@@ -353,48 +415,69 @@ class Alias
$methods = $reflection->getMethods(\ReflectionMethod::IS_PUBLIC);
if ($methods) {
foreach ($methods as $method) {
if (!in_array($method->name, $this->usedMethods)) {
if (!isset($this->usedMethods[$method->name])) {
// Only add the methods to the output when the root is not the same as the class.
// And don't add the __*() methods
if ($this->extends !== $class && substr($method->name, 0, 2) !== '__') {
$this->methods[] = new Method(
$method,
$this->alias,
$reflection,
$method->name,
$this->interfaces,
$this->classAliases
$this->classAliases,
$this->getReturnTypeNormalizers($reflection),
$this->getTemplateNames(),
);
}
$this->usedMethods[] = $method->name;
$this->usedMethods[$method->name] = true;
}
}
}
// Check if the class is macroable
// (Eloquent\Builder is also macroable but doesn't use Macroable trait)
$traits = collect($reflection->getTraitNames());
if ($traits->contains('Illuminate\Support\Traits\Macroable') || $class === EloquentBuilder::class) {
if ($class === EloquentBuilder::class || in_array(Macroable::class, $reflection->getTraitNames())) {
$properties = $reflection->getStaticProperties();
$macros = isset($properties['macros']) ? $properties['macros'] : [];
foreach ($macros as $macro_name => $macro_func) {
if (!in_array($macro_name, $this->usedMethods)) {
if (!isset($this->usedMethods[$macro_name])) {
try {
$method = $this->getMacroFunction($macro_func);
} catch (Throwable $e) {
// Invalid method, skip
continue;
}
// Add macros
$this->methods[] = new Macro(
$this->getMacroFunction($macro_func),
$this->alias,
$method,
$reflection,
$macro_name,
$this->interfaces,
$this->classAliases
$this->classAliases,
$this->getReturnTypeNormalizers($reflection)
);
$this->usedMethods[] = $macro_name;
$this->usedMethods[$macro_name] = true;
}
}
}
}
}
/**
* @param ReflectionClass $class
* @return array<string, string>
*/
protected function getReturnTypeNormalizers($class)
{
if ($this->alias === 'Eloquent' && in_array($class->getName(), [EloquentBuilder::class, QueryBuilder::class])) {
return [
'$this' => '\\' . EloquentBuilder::class . ($this->config->get('ide-helper.use_generics_annotations') ? '<static>' : '|static'),
];
}
return [];
}
/**
* @param $macro_func
*
@@ -442,6 +525,67 @@ class Alias
return $serializer->getDocComment($this->phpdoc);
}
/**
* @param $prefix
* @return string
*/
public function getPhpDocTemplates($prefix = "\t\t")
{
$templateDoc = new DocBlock('');
$serializer = new DocBlockSerializer(1, $prefix);
foreach ($this->getTemplateNames() as $templateName) {
$template = new TemplateTag('template', $templateName);
$template->setBound('static');
$template->setDocBlock($templateDoc);
$templateDoc->appendTag($template);
}
return $serializer->getDocComment($templateDoc);
}
/**
* @return string[]
*/
public function getTemplateNames()
{
if (!isset($this->templateNames)) {
$this->detectTemplateNames();
}
return $this->templateNames;
}
/**
* @return void
* @throws \ReflectionException
*/
protected function detectTemplateNames()
{
$templateNames = [];
foreach ($this->classes as $class) {
$reflection = new ReflectionClass($class);
$traits = collect($reflection->getTraitNames());
$phpdoc = new DocBlock($reflection);
$templates = $phpdoc->getTagsByName('template');
/** @var TemplateTag $template */
foreach ($templates as $template) {
$templateNames[] = $template->getTemplateName();
}
foreach ($traits as $trait) {
$phpdoc = new DocBlock(new ReflectionClass($trait));
$templates = $phpdoc->getTagsByName('template');
/** @var TemplateTag $template */
foreach ($templates as $template) {
$templateNames[] = $template->getTemplateName();
}
}
}
$this->templateNames = $templateNames;
}
/**
* Removes method tags from the doc comment that already appear as functions inside the class.
* This prevents duplicate function errors in the IDE.
@@ -450,12 +594,13 @@ class Alias
*/
protected function removeDuplicateMethodsFromPhpDoc()
{
$methodNames = array_map(function (Method $method) {
return $method->getName();
}, $this->getMethods());
$methodNames = [];
foreach ($this->getMethods() as $method) {
$methodNames[$method->getName()] = true;
}
foreach ($this->phpdoc->getTags() as $tag) {
if ($tag instanceof MethodTag && in_array($tag->getMethodName(), $methodNames)) {
if ($tag instanceof MethodTag && isset($methodNames[$tag->getMethodName()])) {
$this->phpdoc->deleteTag($tag);
}
}
+13 -7
View File
@@ -14,7 +14,9 @@ namespace Barryvdh\LaravelIdeHelper\Console;
use Barryvdh\LaravelIdeHelper\Eloquent;
use Barryvdh\LaravelIdeHelper\Generator;
use Illuminate\Console\Command;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Filesystem\Filesystem;
use Illuminate\View\Factory;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
@@ -45,7 +47,7 @@ class GeneratorCommand extends Command
/** @var Filesystem */
protected $files;
/** @var \Illuminate\View\Factory */
/** @var Factory */
protected $view;
protected $onlyExtend;
@@ -55,14 +57,12 @@ class GeneratorCommand extends Command
*
* @param \Illuminate\Config\Repository $config
* @param Filesystem $files
* @param \Illuminate\View\Factory $view
* @param Factory $view
*/
public function __construct(
/*ConfigRepository */
$config,
Repository $config,
Filesystem $files,
/* Illuminate\View\Factory */
$view
Factory $view
) {
$this->config = $config;
$this->files = $files;
@@ -113,7 +113,12 @@ class GeneratorCommand extends Command
}
$generator = new Generator($this->config, $this->view, $this->getOutput(), $helpers);
$content = $generator->generate();
if ($this->option('eloquent')) {
$content = $generator->generateEloquent();
} else {
$content = $generator->generate();
}
$written = $this->files->put($filename, $content);
if ($written !== false) {
@@ -169,6 +174,7 @@ class GeneratorCommand extends Command
['write_mixins', 'W', InputOption::VALUE_OPTIONAL, 'Write mixins to Laravel Model?', $writeMixins],
['helpers', 'H', InputOption::VALUE_NONE, 'Include the helper files'],
['memory', 'M', InputOption::VALUE_NONE, 'Use sqlite memory driver'],
['eloquent', 'E', InputOption::VALUE_NONE, 'Only write Eloquent methods'],
];
}
}
+227 -28
View File
@@ -11,8 +11,13 @@
namespace Barryvdh\LaravelIdeHelper\Console;
use Barryvdh\LaravelIdeHelper\Factories;
use Dotenv\Parser\Entry;
use Dotenv\Parser\Parser;
use Illuminate\Console\Command;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\View\Factory;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Collection;
use RuntimeException;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
@@ -41,34 +46,54 @@ class MetaCommand extends Command
/** @var \Illuminate\Contracts\Filesystem\Filesystem */
protected $files;
/** @var \Illuminate\Contracts\View\Factory */
/** @var Factory */
protected $view;
/** @var \Illuminate\Contracts\Config\Repository */
/** @var Repository */
protected $config;
protected $methods = [
'new \Illuminate\Contracts\Container\Container',
'\Illuminate\Container\Container::makeWith(0)',
'\Illuminate\Contracts\Container\Container::get(0)',
'\Illuminate\Contracts\Container\Container::make(0)',
'\Illuminate\Contracts\Container\Container::makeWith(0)',
'\App::get(0)',
'\App::make(0)',
'\App::makeWith(0)',
'\app(0)',
'\resolve(0)',
'\Psr\Container\ContainerInterface::get(0)',
'new \Illuminate\Contracts\Container\Container',
'\Illuminate\Container\Container::makeWith(0)',
'\Illuminate\Contracts\Container\Container::get(0)',
'\Illuminate\Contracts\Container\Container::make(0)',
'\Illuminate\Contracts\Container\Container::makeWith(0)',
'\App::get(0)',
'\App::make(0)',
'\App::makeWith(0)',
'\app(0)',
'\resolve(0)',
'\Psr\Container\ContainerInterface::get(0)',
];
protected $configMethods = [
'\config()',
'\Illuminate\Config\Repository::get()',
'\Illuminate\Support\Facades\Config::get()',
];
protected $userMethods = [
'\auth()->user()',
'\Illuminate\Contracts\Auth\Guard::user()',
'\Illuminate\Support\Facades\Auth::user()',
'\request()->user()',
'\Illuminate\Http\Request::user()',
'\Illuminate\Support\Facades\Request::user()',
];
protected $templateCache = [];
/**
*
* @param \Illuminate\Contracts\Filesystem\Filesystem $files
* @param \Illuminate\Contracts\View\Factory $view
* @param \Illuminate\Contracts\Config\Repository $config
* @param Filesystem $files
* @param Factory $view
* @param Repository $config
*/
public function __construct($files, $view, $config)
{
public function __construct(
Filesystem $files,
Factory $view,
Repository $config
) {
$this->files = $files;
$this->view = $view;
$this->config = $config;
@@ -82,9 +107,6 @@ class MetaCommand extends Command
*/
public function handle()
{
// Needs to run before exception handler is registered
$factories = $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : [];
$ourAutoloader = $this->registerClassAutoloadExceptions();
$bindings = [];
@@ -102,7 +124,7 @@ class MetaCommand extends Command
}
$reflectionClass = new \ReflectionClass($concrete);
if (is_object($concrete) && !$reflectionClass->isAnonymous()) {
if (is_object($concrete) && !$reflectionClass->isAnonymous() && $abstract !== get_class($concrete)) {
$bindings[$abstract] = get_class($concrete);
}
} catch (\Throwable $e) {
@@ -114,10 +136,21 @@ class MetaCommand extends Command
$this->unregisterClassAutoloadExceptions($ourAutoloader);
$content = $this->view->make('meta', [
'bindings' => $bindings,
'methods' => $this->methods,
'factories' => $factories,
$configValues = $this->loadTemplate('configs')->pluck('value', 'name')->map(function ($value, $key) {
return gettype($value);
});
$defaultUserModel = $this->config->get('auth.providers.users.model', $this->config->get('auth.model', 'App\User'));
$content = $this->view->make('ide-helper::meta', [
'bindings' => $bindings,
'methods' => $this->methods,
'configMethods' => $this->configMethods,
'configValues' => $configValues,
'expectedArgumentSets' => $this->getExpectedArgumentSets(),
'expectedArguments' => $this->getExpectedArguments(),
'userModel' => $defaultUserModel,
'userMethods' => $this->userMethods,
])->render();
$filename = $this->option('filename');
@@ -154,13 +187,164 @@ class MetaCommand extends Command
*/
protected function registerClassAutoloadExceptions(): callable
{
$autoloader = function ($class) {
$aliases = array_filter([...$this->getAbstracts(), 'config'], fn ($abstract) => !str_contains($abstract, '\\'));
$autoloader = function ($class) use ($aliases) {
// ignore aliases as they're meant to be resolved elsewhere
if (in_array($class, $aliases, true)) {
return;
}
// Don't throw when class existence is being checked via class_exists(),
// interface_exists(), trait_exists(), or enum_exists(). These functions
// expect the autoloader to return gracefully when the class doesn't exist.
// Throwing here would break libraries that use class_exists() to check for
// optional dependencies (e.g. Doctrine ORM checking for removed classes).
$existsFunctions = ['class_exists', 'interface_exists', 'trait_exists', 'enum_exists'];
foreach (debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3) as $frame) {
if (isset($frame['function']) && in_array($frame['function'], $existsFunctions, true)) {
return;
}
}
throw new \ReflectionException("Class '$class' not found.");
};
spl_autoload_register($autoloader);
return $autoloader;
}
protected function getExpectedArgumentSets()
{
return [
'auth' => $this->loadTemplate('auth')->keys()->filter()->toArray(),
'configs' => $this->loadTemplate('configs')->pluck('name')->filter()->toArray(),
'middleware' => $this->loadTemplate('middleware')->keys()->filter()->toArray(),
'routes' => $this->loadTemplate('routes')->pluck('name')->filter()->toArray(),
'views' => $this->loadTemplate('views')->pluck('key')->filter()->map(function ($value) {
return (string) $value;
})->toArray(),
'translations' => $this->loadTemplate('translations')->filter()->keys()->toArray(),
'env' => $this->getEnv(),
];
}
protected function getExpectedArguments()
{
return [
[
'class' => '\Illuminate\Support\Facades\Gate',
'method' => [
'has',
'allows',
'denies',
'check',
'any',
'none',
'authorize',
'inspect',
],
'argumentSet' => 'auth',
],
[
'class' => ['\Illuminate\Support\Facades\Route', '\Illuminate\Support\Facades\Auth', 'Illuminate\Foundation\Auth\Access\Authorizable'],
'method' => ['can', 'cannot', 'cant'],
'argumentSet' => 'auth',
],
[
'class' => ['Illuminate\Contracts\Auth\Access\Authorizable'],
'method' => ['can'],
'argumentSet' => 'auth',
],
[
'class' => ['\Illuminate\Config\Repository', '\Illuminate\Support\Facades\Config'],
'method' => [
// 'get', // config() and Config::Get() are added with return type hints already
'getMany',
'set',
'string',
'integer',
'boolean',
'float',
'array',
'prepend',
'push',
],
'argumentSet' => 'configs',
],
[
'class' => ['\Illuminate\Support\Facades\Route', '\Illuminate\Routing\Router'],
'method' => ['middleware', 'withoutMiddleware'],
'argumentSet' => 'middleware',
],
[
'method' => ['route', 'to_route', 'signedRoute'],
'argumentSet' => 'routes',
],
[
'class' => [
'\Illuminate\Support\Facades\Redirect',
'\Illuminate\Support\Facades\URL',
'\Illuminate\Routing\Redirector',
'\Illuminate\Routing\UrlGenerator',
],
'method' => ['route', 'signedRoute', 'temporarySignedRoute'],
'argumentSet' => 'routes',
],
[
'method' => 'view',
'argumentSet' => 'views',
],
[
'class' => ['\Illuminate\Support\Facades\View', '\Illuminate\View\Factory'],
'method' => 'make',
'argumentSet' => 'views',
],
[
'method' => ['__', 'trans'],
'argumentSet' => 'translations',
],
[
'class' => ['\Illuminate\Contracts\Translation\Translator'],
'method' => ['get'],
'argumentSet' => 'translations',
],
[
'method' => 'env',
'argumentSet' => 'env',
],
[
'class' => '\Illuminate\Support\Env',
'method' => 'get',
'argumentSet' => 'env',
],
];
}
/**
* @return Collection
*/
protected function loadTemplate($name)
{
if (!isset($this->templateCache[$name])) {
$file = __DIR__ . '/../../php-templates/' . basename($name) . '.php';
try {
$value = $this->files->requireOnce($file) ?: [];
} catch (\Throwable $e) {
$value = [];
$this->warn('Cannot load template for ' . $name . ': ' . $e->getMessage());
}
if (!$value instanceof Collection) {
$value = collect($value);
}
$this->templateCache[$name] = $value;
}
return $this->templateCache[$name];
}
/**
* Get the console command options.
*
@@ -175,6 +359,21 @@ class MetaCommand extends Command
];
}
protected function getEnv()
{
$envPath = base_path('.env');
if (!file_exists($envPath)) {
return [];
}
$parser = new Parser();
$entries = $parser->parse(file_get_contents($envPath));
return collect($entries)->map(function (Entry $entry) {
return $entry->getName();
});
}
/**
* Remove our custom autoloader that we pushed onto the autoload stack
*
+323 -65
View File
@@ -12,13 +12,16 @@
namespace Barryvdh\LaravelIdeHelper\Console;
use Barryvdh\LaravelIdeHelper\Contracts\ModelHookInterface;
use Barryvdh\LaravelIdeHelper\Generator;
use Barryvdh\LaravelIdeHelper\Parsers\PhpDocReturnTypeParser;
use Barryvdh\Reflection\DocBlock;
use Barryvdh\Reflection\DocBlock\Context;
use Barryvdh\Reflection\DocBlock\ContextFactory;
use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
use Barryvdh\Reflection\DocBlock\Tag;
use Composer\ClassMapGenerator\ClassMapGenerator;
use Illuminate\Console\Command;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Database\Eloquent\Castable;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes;
@@ -41,12 +44,13 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Database\Schema\Builder;
use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use phpDocumentor\Reflection\Types\ContextFactory;
use Illuminate\View\Factory as ViewFactory;
use ReflectionClass;
use ReflectionNamedType;
use ReflectionObject;
@@ -82,6 +86,14 @@ class ModelsCommand extends Command
*/
protected $files;
/**
* @var Repository
*/
protected $config;
/** @var ViewFactory */
protected $view;
/**
* The console command name.
*
@@ -103,6 +115,7 @@ class ModelsCommand extends Command
protected $write_model_magic_where;
protected $write_model_relation_count_properties;
protected $write_model_relation_exists_properties;
protected $properties = [];
protected $methods = [];
protected $write = false;
@@ -111,6 +124,14 @@ class ModelsCommand extends Command
protected $reset;
protected $phpstorm_noinspections;
protected $write_model_external_builder_methods;
/**
* @var array<string, \SplFileObject>
*/
protected $fileCache = [];
/**
* @var array<string, Context>
*/
protected $contextCache = [];
/**
* @var array<string, true>
*/
@@ -131,10 +152,12 @@ class ModelsCommand extends Command
/**
* @param Filesystem $files
*/
public function __construct(Filesystem $files)
public function __construct(Filesystem $files, Repository $config, ViewFactory $view)
{
parent::__construct();
$this->config = $config;
$this->files = $files;
$this->view = $view;
parent::__construct();
}
/**
@@ -160,6 +183,8 @@ class ModelsCommand extends Command
$this->write_model_external_builder_methods = $this->laravel['config']->get('ide-helper.write_model_external_builder_methods', true);
$this->write_model_relation_count_properties =
$this->laravel['config']->get('ide-helper.write_model_relation_count_properties', true);
$this->write_model_relation_exists_properties =
$this->laravel['config']->get('ide-helper.write_model_relation_exists_properties', false);
$this->write = $this->write_mixin ? true : $this->write;
//If filename is default and Write is not specified, ask what to do
@@ -187,6 +212,27 @@ class ModelsCommand extends Command
$this->error("Failed to write model information to $filename");
}
}
$helperFilename = $this->config->get('ide-helper.filename');
$writeHelper = $this->option('write-eloquent-helper');
if (!$writeHelper && !$this->files->exists($helperFilename) && ($this->write || $this->write_mixin)) {
if ($this->confirm("{$helperFilename} does not exist.
Do you want to generate a minimal helper to generate the Eloquent methods?")) {
$writeHelper = true;
}
}
if ($writeHelper) {
$generator = new Generator($this->config, $this->view, $this->getOutput());
$content = $generator->generateEloquent();
$written = $this->files->put($helperFilename, $content);
if ($written !== false) {
$this->info("Eloquent helper was written to $helperFilename");
} else {
$this->error("Failed to write eloquent helper to $helperFilename");
}
}
}
@@ -217,9 +263,12 @@ class ModelsCommand extends Command
['write-mixin', 'M', InputOption::VALUE_NONE,
"Write models to {$this->filename} and adds @mixin to each model, avoiding IDE duplicate declaration warnings",
],
['write-eloquent-helper', 'E', InputOption::VALUE_NONE,
'Write Eloquent helper file to _ide_helper.php',
],
['nowrite', 'N', InputOption::VALUE_NONE, 'Don\'t write to Model file'],
['reset', 'R', InputOption::VALUE_NONE, 'Remove the original phpdocs instead of appending'],
['smart-reset', 'r', InputOption::VALUE_NONE, 'Refresh the properties/methods list, but keep the text'],
['smart-reset', 'r', InputOption::VALUE_NONE, 'Retained for compatibility, while it no longer has any effect'],
['phpstorm-noinspections', 'p', InputOption::VALUE_NONE,
'Add PhpFullyQualifiedNameUsageInspection and PhpUnnecessaryFullyQualifiedNameInspection PHPStorm ' .
'noinspection tags',
@@ -373,21 +422,21 @@ class ModelsCommand extends Command
$params = [];
switch ($type) {
case 'encrypted':
$realType = 'mixed';
break;
case 'boolean':
case 'bool':
$realType = 'bool';
break;
case 'decimal':
$realType = 'numeric';
break;
case 'encrypted':
case 'string':
case 'hashed':
$realType = 'string';
break;
case 'array':
case 'json':
$realType = 'array';
$realType = 'array<array-key, mixed>';
break;
case 'object':
$realType = 'object';
@@ -410,13 +459,11 @@ class ModelsCommand extends Command
case 'immutable_datetime':
$realType = '\Carbon\CarbonImmutable';
break;
case AsCollection::class:
case AsEnumCollection::class:
case 'collection':
$realType = '\Illuminate\Support\Collection';
$realType = '\Illuminate\Support\Collection<array-key, mixed>';
break;
case AsArrayObject::class:
$realType = '\ArrayObject';
$realType = '\Illuminate\Database\Eloquent\Casts\ArrayObject<array-key, mixed>';
break;
default:
// In case of an optional custom cast parameter , only evaluate
@@ -437,6 +484,22 @@ class ModelsCommand extends Command
continue;
}
if (Str::startsWith($type, AsCollection::class)) {
$realType = $this->getTypeInModel($model, $params[0] ?? null) ?: '\Illuminate\Support\Collection';
$relatedModel = $this->getTypeInModel($model, $params[1] ?? null);
if ($relatedModel) {
$realType = $this->getCollectionTypeHint($realType, $relatedModel);
}
}
if (Str::startsWith($type, AsEnumCollection::class)) {
$realType = '\Illuminate\Support\Collection';
$relatedModel = $this->getTypeInModel($model, $params[0] ?? null);
if ($relatedModel) {
$realType = $this->getCollectionTypeHint($realType, $relatedModel);
}
}
$realType = $this->checkForCastableCasts($realType, $params);
$realType = $this->checkForCustomLaravelCasts($realType);
$realType = $this->getTypeOverride($realType);
@@ -472,7 +535,7 @@ class ModelsCommand extends Command
}
if ($isNullable) {
$type .= '|null';
$type = $this->wrapIntersectionType($type) . '|null';
} else {
$type = str_replace($nullString, '', $type);
}
@@ -480,6 +543,22 @@ class ModelsCommand extends Command
return $type;
}
/**
* Wraps a bare intersection type in parentheses for correct DNF syntax.
*
* For example, `A&B` becomes `(A&B)` so that adding `|null` produces
* `(A&B)|null` instead of the ambiguous `A&B|null`.
* Types that are already parenthesized or contain union types are returned as-is.
*/
protected function wrapIntersectionType(string $type): string
{
if (str_contains($type, '&') && !str_contains($type, '|') && $type[0] !== '(') {
return '(' . $type . ')';
}
return $type;
}
/**
* Returns the override type for the give type.
*
@@ -530,6 +609,8 @@ class ModelsCommand extends Command
'float', 'real', 'float4',
'double', 'float8' => 'float',
'decimal', 'numeric' => 'numeric',
default => 'string',
};
}
@@ -579,6 +660,10 @@ class ModelsCommand extends Command
)
);
});
// https://github.com/barryvdh/laravel-ide-helper/issues/1664
$reflections = array_filter($reflections, function (\ReflectionMethod $methodReflection) {
return !($methodReflection->getName() === 'getUseFactoryAttribute');
});
sort($reflections);
foreach ($reflections as $reflection) {
$type = $this->getReturnTypeFromReflection($reflection);
@@ -616,9 +701,11 @@ class ModelsCommand extends Command
$comment = $this->getCommentFromDocBlock($reflection);
$this->setProperty($name, null, null, true, $comment);
}
} elseif (Str::startsWith($method, 'scope') && $method !== 'scopeQuery' && $method !== 'scope' && $method !== 'scopes') {
} elseif (!empty($reflection->getAttributes('Illuminate\Database\Eloquent\Attributes\Scope')) || (Str::startsWith($method, 'scope') && $method !== 'scopeQuery' && $method !== 'scope' && $method !== 'scopes')) {
$scopeUsingAttribute = !empty($reflection->getAttributes('Illuminate\Database\Eloquent\Attributes\Scope'));
//Magic scope<name>Attribute
$name = Str::camel(substr($method, 5));
$name = $scopeUsingAttribute ? $method : Str::camel(substr($method, 5));
if (!empty($name)) {
$comment = $this->getCommentFromDocBlock($reflection);
$args = $this->getParameters($reflection);
@@ -634,13 +721,16 @@ class ModelsCommand extends Command
);
$this->setMethod($name, $builder . '<static>|' . $modelName, $args, $comment);
}
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])) {
$builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery()));
} elseif (in_array($method, ['query', 'newQuery', 'newModelQuery'])
) {
if ($this->laravel['config']->get('ide-helper.write_query_methods', true)) {
$builder = $this->getClassNameInDestinationFile($model, get_class($model->newModelQuery()));
$this->setMethod(
$method,
$builder . '<static>|' . $this->getClassNameInDestinationFile($model, get_class($model))
);
$this->setMethod(
$method,
$builder . '<static>|' . $this->getClassNameInDestinationFile($model, get_class($model))
);
}
if ($this->write_model_external_builder_methods) {
$this->writeModelExternalBuilderMethods($model);
@@ -659,14 +749,19 @@ class ModelsCommand extends Command
$type = (string)$this->getReturnTypeFromDocBlock($reflection);
}
$file = new \SplFileObject($reflection->getFileName());
$fileName = $reflection->getFileName();
if (!isset($this->fileCache[$fileName])) {
$this->fileCache[$fileName] = new \SplFileObject($fileName);
}
$file = $this->fileCache[$fileName];
$file->seek($reflection->getStartLine() - 1);
$code = '';
$lines = [];
while ($file->key() < $reflection->getEndLine()) {
$code .= $file->current();
$lines[] = $file->current();
$file->next();
}
$code = implode('', $lines);
$code = trim(preg_replace('/\s\s+/', '', $code));
$begin = strpos($code, 'function(');
$code = substr($code, $begin, strrpos($code, '}') - $begin + 1);
@@ -708,20 +803,37 @@ class ModelsCommand extends Command
$relationReturnType === 'many' ||
(
!$relationReturnType &&
strpos(get_class($relationObj), 'Many') !== false
str_contains(get_class($relationObj), 'Many')
)
) {
if ($relationObj instanceof BelongsToMany) {
$pivot = get_class($relationObj->newPivot());
if (!in_array($pivot, [Pivot::class, MorphPivot::class])) {
$pivot = $this->getClassNameInDestinationFile($model, $pivot);
if ($existingPivot = ($this->properties[$relationObj->getPivotAccessor()] ?? null)) {
$existingClasses = explode('|', $existingPivot['type']);
if (!in_array($pivot, $existingClasses)) {
array_unshift($existingClasses, $pivot);
}
} else {
// No existing pivot property, so we need to add a null type
$existingClasses = [$pivot, 'null'];
}
// create a union type of all pivot classes
$unionType = implode('|', $existingClasses);
$this->setProperty(
$relationObj->getPivotAccessor(),
$this->getClassNameInDestinationFile($model, $pivot),
$unionType,
true,
false
);
}
}
//Collection or array of models (because Collection is Arrayable)
$relatedClass = '\\' . get_class($relationObj->getRelated());
$collectionClass = $this->getCollectionClass($relatedClass);
@@ -746,6 +858,15 @@ class ModelsCommand extends Command
// What kind of comments should be added to the relation count here?
);
}
if ($this->write_model_relation_exists_properties) {
$this->setProperty(
Str::snake($method) . '_exists',
'bool|null',
true,
false
// What kind of comments should be added to the relation count here?
);
}
} elseif (
$relationReturnType === 'morphTo' ||
(
@@ -753,10 +874,16 @@ class ModelsCommand extends Command
$relation === 'morphTo'
)
) {
$matches = [];
$returnType = $this->getReturnTypeFromDocBlock($reflection);
if ($returnType !== null) {
preg_match('/MorphTo<(?:contravariant\s+)?(.+?)(?:,|>)/i', $returnType, $matches);
}
// Model isn't specified because relation is polymorphic
$this->setProperty(
$method,
$this->getClassNameInDestinationFile($model, Model::class) . '|\Eloquent',
$matches[1] ?? $this->getClassNameInDestinationFile($model, Model::class) . '|\Eloquent',
true,
null,
$comment,
@@ -795,7 +922,6 @@ class ModelsCommand extends Command
if (in_array($relation, ['hasOne', 'hasOneThrough', 'morphOne'], true)) {
$defaultProp = $reflectionObj->getProperty('withDefault');
$defaultProp->setAccessible(true);
return !$defaultProp->getValue($relationObj);
}
@@ -805,7 +931,8 @@ class ModelsCommand extends Command
}
$fkProp = $reflectionObj->getProperty('foreignKey');
$fkProp->setAccessible(true);
$enforceNullableRelation = $this->laravel['config']->get('ide-helper.enforce_nullable_relationships', true);
foreach (Arr::wrap($fkProp->getValue($relationObj)) as $foreignKey) {
if (isset($this->nullableColumns[$foreignKey])) {
@@ -813,13 +940,77 @@ class ModelsCommand extends Command
}
if (!in_array($foreignKey, $this->foreignKeyConstraintsColumns, true)) {
return true;
return $enforceNullableRelation;
}
}
if (
$this->relatedModelUsesSoftDeletes($relationObj)
&& !$this->relationIncludesNonTrashedParents($relationObj)
) {
return true;
}
return false;
}
/**
* Check whether the relation explicitly opts into returning non-soft-deleted parents
* via ->withTrashed(), in which case the SoftDeletes-based nullability no longer applies.
*
* Returns false for ->onlyTrashed() and ->withoutTrashed(), which still leave the
* relation potentially empty depending on the parent's soft-delete state.
*
* @param Relation $relationObj
*
* @return bool
*/
protected function relationIncludesNonTrashedParents(Relation $relationObj): bool
{
$query = $relationObj->getQuery();
if (!in_array(SoftDeletingScope::class, $query->removedScopes(), true)) {
return false;
}
$relatedModel = $relationObj->getRelated();
if (!method_exists($relatedModel, 'getQualifiedDeletedAtColumn')) {
return true;
}
$deletedAtColumn = $relatedModel->getQualifiedDeletedAtColumn();
foreach ($query->getQuery()->wheres ?? [] as $where) {
if (
($where['column'] ?? null) === $deletedAtColumn
&& in_array($where['type'] ?? null, ['Null', 'NotNull'], true)
) {
return false;
}
}
return true;
}
/**
* Check if the related model uses the SoftDeletes trait
*
* @param Relation $relationObj
*
* @return bool
*/
protected function relatedModelUsesSoftDeletes(Relation $relationObj): bool
{
if (!$this->laravel['config']->get('ide-helper.soft_deletes_force_nullable', true)) {
return false;
}
$relatedModel = $relationObj->getRelated();
return in_array('Illuminate\\Database\\Eloquent\\SoftDeletes', class_uses_recursive($relatedModel));
}
/**
* Check if the morphTo relation is nullable
*
@@ -836,7 +1027,6 @@ class ModelsCommand extends Command
}
$fkProp = $reflectionObj->getProperty('foreignKey');
$fkProp->setAccessible(true);
foreach (Arr::wrap($fkProp->getValue($relationObj)) as $foreignKey) {
if (isset($this->nullableColumns[$foreignKey])) {
@@ -867,7 +1057,7 @@ class ModelsCommand extends Command
if ($type !== null) {
$newType = $this->getTypeOverride($type);
if ($nullable) {
$newType .= '|null';
$newType = $this->wrapIntersectionType($newType) . '|null';
}
$this->properties[$name]['type'] = $newType;
}
@@ -924,13 +1114,19 @@ class ModelsCommand extends Command
$reflection->getParentClass()->getInterfaceNames()
);
$phpdoc = new DocBlock($reflection, new Context($namespace));
if ($this->reset) {
$phpdoc = new DocBlock('', new Context($namespace));
$phpdoc->setText(
(new DocBlock($reflection, new Context($namespace)))->getText()
);
} else {
$phpdoc = new DocBlock($reflection, new Context($namespace));
foreach ($phpdoc->getTags() as $tag) {
if (
in_array($tag->getName(), ['property', 'property-read', 'property-write', 'method', 'mixin'])
|| ($tag->getName() === 'noinspection' && in_array($tag->getContent(), ['PhpUnnecessaryFullyQualifiedNameInspection', 'PhpFullyQualifiedNameUsageInspection']))
) {
$phpdoc->deleteTag($tag);
}
}
}
$properties = [];
@@ -1012,6 +1208,7 @@ class ModelsCommand extends Command
$serializer = new DocBlockSerializer();
$docComment = $serializer->getDocComment($phpdoc);
$mixinClassName = null;
if ($this->write_mixin) {
$phpdocMixin = new DocBlock($reflection, new Context($namespace));
@@ -1048,6 +1245,14 @@ class ModelsCommand extends Command
$replace = "{$modelDocComment}\n";
$pos = strpos($contents, "final class {$classname}") ?: strpos($contents, "class {$classname}");
if ($pos !== false) {
// If PHP 8 attributes (e.g. #[ObservedBy(...)]) precede the class
// declaration, insert the docblock before the first attribute so that
// the resulting order is: docblock → attributes → class.
$before = substr($contents, 0, $pos);
if (preg_match('/((?:#\[.+?\]\s*)+)$/s', $before, $matches)) {
$pos -= strlen($matches[1]);
$replace = "{$modelDocComment}\n";
}
$contents = substr_replace($contents, $replace, $pos, 0);
}
}
@@ -1100,7 +1305,7 @@ class ModelsCommand extends Command
$default = '[]';
} elseif (is_null($default)) {
$default = 'null';
} elseif (is_int($default)) {
} elseif (is_int($default) || is_float($default)) {
//$default = $default;
} elseif ($default instanceof \UnitEnum) {
$default = '\\' . get_class($default) . '::' . $default->name;
@@ -1153,13 +1358,18 @@ class ModelsCommand extends Command
}
}
protected ?array $cachedRelationTypes = null;
protected ?array $cachedRelationReturnTypes = null;
/**
* Returns the available relation types
*/
protected function getRelationTypes(): array
{
$configuredRelations = $this->laravel['config']->get('ide-helper.additional_relation_types', []);
return array_merge(self::RELATION_TYPES, $configuredRelations);
return $this->cachedRelationTypes ??= array_merge(
self::RELATION_TYPES,
$this->laravel['config']->get('ide-helper.additional_relation_types', [])
);
}
/**
@@ -1167,7 +1377,7 @@ class ModelsCommand extends Command
*/
protected function getRelationReturnTypes(): array
{
return $this->laravel['config']->get('ide-helper.additional_relation_return_types', []);
return $this->cachedRelationReturnTypes ??= $this->laravel['config']->get('ide-helper.additional_relation_return_types', []);
}
/**
@@ -1183,9 +1393,6 @@ class ModelsCommand extends Command
*/
protected function getAttributeTypes(Model $model, \ReflectionMethod $reflectionMethod): Collection
{
// Private/protected ReflectionMethods require setAccessible prior to PHP 8.1
$reflectionMethod->setAccessible(true);
/** @var Attribute $attribute */
$attribute = $reflectionMethod->invoke($model);
@@ -1245,11 +1452,7 @@ class ModelsCommand extends Command
*/
protected function getCommentFromDocBlock(\ReflectionMethod $reflection)
{
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
$context = new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
);
$context = $this->getDocBlockContext($reflection);
$comment = '';
$phpdoc = new DocBlock($reflection, $context);
@@ -1264,16 +1467,13 @@ class ModelsCommand extends Command
* Get method return type based on it DocBlock comment
*
* @param \ReflectionMethod $reflection
* @param ?\Reflector $reflectorForContext
*
* @return null|string
*/
protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection, \Reflector $reflectorForContext = null)
protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection, ?\Reflector $reflectorForContext = null)
{
$phpDocContext = (new ContextFactory())->createFromReflector($reflectorForContext ?? $reflection);
$context = new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
);
$context = $this->getDocBlockContext($reflectorForContext ?? $reflection);
$type = null;
$phpdoc = new DocBlock($reflection, $context);
@@ -1291,6 +1491,31 @@ class ModelsCommand extends Command
return $type;
}
protected function getDocBlockContext(\Reflector $reflector): Context
{
if ($reflector instanceof \ReflectionMethod) {
$key = $reflector->getDeclaringClass()->getName();
} elseif ($reflector instanceof ReflectionClass) {
$key = $reflector->getName();
} else {
$phpDocContext = (new ContextFactory())->createFromReflector($reflector);
return new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
);
}
if (!isset($this->contextCache[$key])) {
$phpDocContext = (new ContextFactory())->createFromReflector($reflector);
$this->contextCache[$key] = new Context(
$phpDocContext->getNamespace(),
$phpDocContext->getNamespaceAliases()
);
}
return $this->contextCache[$key];
}
protected function getReturnTypeFromReflection(\ReflectionMethod $reflection): ?string
{
$returnType = $reflection->getReturnType();
@@ -1320,7 +1545,7 @@ class ModelsCommand extends Command
if (in_array('Illuminate\\Database\\Eloquent\\SoftDeletes', $traits)) {
$modelName = $this->getClassNameInDestinationFile($model, get_class($model));
$builder = $this->getClassNameInDestinationFile($model, \Illuminate\Database\Eloquent\Builder::class);
$this->setMethod('withTrashed', $builder . '<static>|' . $modelName, []);
$this->setMethod('withTrashed', $builder . '<static>|' . $modelName, ['bool $withTrashed = true']);
$this->setMethod('withoutTrashed', $builder . '<static>|' . $modelName, []);
$this->setMethod('onlyTrashed', $builder . '<static>|' . $modelName, []);
}
@@ -1496,7 +1721,10 @@ class ModelsCommand extends Command
*/
protected function getUsedClassNames(ReflectionClass $reflection): array
{
$namespaceAliases = array_flip((new ContextFactory())->createFromReflector($reflection)->getNamespaceAliases());
$context = $this->getDocBlockContext($reflection);
$namespaceAliases = array_flip(array_map(function ($alias) {
return ltrim($alias, '\\');
}, $context->getNamespaceAliases()));
$namespaceAliases[$reflection->getName()] = $reflection->getShortName();
return $namespaceAliases;
@@ -1540,7 +1768,8 @@ class ModelsCommand extends Command
$type = implode('|', $types);
if ($paramType->allowsNull()) {
if (count($types) == 1) {
// Use ?Type syntax only for single named types, not for intersection types
if (count($types) == 1 && !str_starts_with($type, '(')) {
$type = '?' . $type;
} else {
$type .= '|null';
@@ -1558,7 +1787,7 @@ class ModelsCommand extends Command
preg_match(
'/@param ((?:(?:[\w?|\\\\<>])+(?:\[])?)+)/',
$docComment ?? '',
$docComment,
$matches
);
$type = $matches[1] ?? '';
@@ -1613,24 +1842,53 @@ class ModelsCommand extends Command
return $type;
}
protected function extractReflectionTypes(ReflectionType $reflection_type)
protected function extractReflectionTypes(ReflectionType $reflection_type): array
{
if ($reflection_type instanceof ReflectionNamedType) {
$types[] = $this->getReflectionNamedType($reflection_type);
} else {
$types = [];
foreach ($reflection_type->getTypes() as $named_type) {
if ($named_type->getName() === 'null') {
return [$this->getReflectionNamedType($reflection_type)];
}
if ($reflection_type instanceof \ReflectionIntersectionType) {
return [$this->formatIntersectionType($reflection_type)];
}
if ($reflection_type instanceof \ReflectionUnionType) {
return $this->extractUnionTypes($reflection_type);
}
// Unknown type - return empty array as fallback
return [];
}
protected function extractUnionTypes(\ReflectionUnionType $union_type): array
{
$types = [];
foreach ($union_type->getTypes() as $inner_type) {
if ($inner_type instanceof ReflectionNamedType) {
if ($inner_type->getName() === 'null') {
continue;
}
$types[] = $this->getReflectionNamedType($named_type);
$types[] = $this->getReflectionNamedType($inner_type);
} elseif ($inner_type instanceof \ReflectionIntersectionType) {
$types[] = $this->formatIntersectionType($inner_type);
}
// ReflectionUnionType cannot be nested per PHP's DNF rules
}
return $types;
}
protected function formatIntersectionType(\ReflectionIntersectionType $intersection_type): string
{
$parts = [];
foreach ($intersection_type->getTypes() as $type) {
$parts[] = $this->getReflectionNamedType($type);
}
return '(' . implode('&', $parts) . ')';
}
protected function getReflectionNamedType(ReflectionNamedType $paramType): string
{
$parameterName = $paramType->getName();
-36
View File
@@ -1,36 +0,0 @@
<?php
namespace Barryvdh\LaravelIdeHelper;
use Exception;
use Illuminate\Database\Eloquent\Factory;
use ReflectionClass;
class Factories
{
public static function all()
{
$factories = [];
if (static::isLaravelSevenOrLower()) {
$factory = app(Factory::class);
$definitions = (new ReflectionClass(Factory::class))->getProperty('definitions');
$definitions->setAccessible(true);
foreach ($definitions->getValue($factory) as $factory_target => $config) {
try {
$factories[] = new ReflectionClass($factory_target);
} catch (Exception $exception) {
}
}
}
return $factories;
}
protected static function isLaravelSevenOrLower()
{
return class_exists('Illuminate\Database\Eloquent\Factory');
}
}
+71 -34
View File
@@ -11,15 +11,12 @@
namespace Barryvdh\LaravelIdeHelper;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\AliasLoader;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Facade;
use Illuminate\Support\Str;
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 Symfony\Component\Console\Output\OutputInterface;
@@ -42,7 +39,7 @@ class Generator
/**
* @param \Illuminate\Config\Repository $config
* @param \Illuminate\View\Factory $view
* @param OutputInterface $output
* @param ?OutputInterface $output
* @param string $helpers
*/
public function __construct(
@@ -50,18 +47,23 @@ class Generator
$config,
/* Illuminate\View\Factory */
$view,
OutputInterface $output = null,
?OutputInterface $output = null,
$helpers = ''
) {
$this->config = $config;
$this->view = $view;
$this->output = $output;
// Find the drivers to add to the extra/interfaces
$this->detectDrivers();
$this->extra = array_merge($this->extra, $this->config->get('ide-helper.extra'), []);
$this->magic = array_merge($this->magic, $this->config->get('ide-helper.magic'), []);
$this->interfaces = array_merge($this->interfaces, $this->config->get('ide-helper.interfaces'), []);
$this->extra = array_merge($this->extra, $this->config->get('ide-helper.extra', []));
$this->magic = array_merge($this->magic, $this->config->get('ide-helper.magic', []));
$this->interfaces = array_merge($this->interfaces, $this->config->get('ide-helper.interfaces', []));
Macro::setDefaultReturnTypes($this->config->get('ide-helper.macro_default_return_types', [
\Illuminate\Http\Client\Factory::class => \Illuminate\Http\Client\PendingRequest::class,
]));
// Make all interface classes absolute
foreach ($this->interfaces as &$interface) {
$interface = '\\' . ltrim($interface, '\\');
@@ -72,22 +74,64 @@ class Generator
/**
* Generate the helper file contents;
*
* @return string;
* @return string
*/
public function generate()
{
$app = app();
return $this->view->make('helper')
return $this->view->make('ide-helper::helper')
->with('namespaces_by_extends_ns', $this->getAliasesByExtendsNamespace())
->with('namespaces_by_alias_ns', $this->getAliasesByAliasNamespace())
->with('real_time_facades', $this->getRealTimeFacades())
->with('helpers', $this->helpers)
->with('version', $app->version())
->with('helpers', $this->detectHelpers())
->with('include_fluent', $this->config->get('ide-helper.include_fluent', true))
->with('factories', $this->config->get('ide-helper.include_factory_builders') ? Factories::all() : [])
->render();
}
public function generateEloquent()
{
$name = 'Eloquent';
$facade = Model::class;
$magicMethods = array_key_exists($name, $this->magic) ? $this->magic[$name] : [];
$alias = new Alias($this->config, $name, $facade, $magicMethods, $this->interfaces);
if (!$alias->isValid()) {
throw new \RuntimeException('Cannot generate Eloquent helper');
}
//Add extra methods, from other classes (magic static calls)
if (array_key_exists($name, $this->extra)) {
$alias->addClass($this->extra[$name]);
}
$app = app();
return $this->view->make('ide-helper::helper')
->with('namespaces_by_extends_ns', [])
->with('namespaces_by_alias_ns', ['__root' => [$alias]])
->with('real_time_facades', [])
->with('helpers', '')
->with('include_fluent', false)
->with('factories', [])
->render();
}
protected function detectHelpers()
{
$helpers = $this->helpers;
$replacements = [
'($guard is null ? \Illuminate\Contracts\Auth\Factory : \Illuminate\Contracts\Auth\StatefulGuard)' => '\\Auth',
];
foreach ($replacements as $search => $replace) {
$helpers = Str::replace(
"@return {$search}",
"@return $replace|$search",
$helpers
);
}
return $helpers;
}
protected function detectDrivers()
{
$defaultUserModel = config('auth.providers.users.model', config('auth.model', 'App\User'));
@@ -186,8 +230,10 @@ class Generator
foreach ($realTimeFacadeFiles as $file) {
try {
$name = $this->getFullyQualifiedClassNameInFile($file);
$facades[$name] = $name;
} catch (\Exception $e) {
if ($name) {
$facades[$name] = $name;
}
} catch (\Throwable $e) {
continue;
}
}
@@ -199,26 +245,17 @@ class Generator
{
$contents = file_get_contents($path);
$parsers = new Php7(new Emulative());
// Match namespace
preg_match('/namespace\s+([^;]+);/', $contents, $namespaceMatch);
$namespace = isset($namespaceMatch[1]) ? $namespaceMatch[1] : '';
$parsed = collect($parsers->parse($contents) ?: []);
// Match class name
preg_match('/class\s+([a-zA-Z0-9_]+)/', $contents, $classMatch);
$className = isset($classMatch[1]) ? $classMatch[1] : '';
$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;
// Combine namespace and class name
if ($namespace && $className) {
return $namespace . '\\' . $className;
}
}
+12 -54
View File
@@ -20,10 +20,6 @@ use Illuminate\Console\Events\CommandFinished;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Database\Events\MigrationsEnded;
use Illuminate\Support\ServiceProvider;
use Illuminate\View\Engines\EngineResolver;
use Illuminate\View\Engines\PhpEngine;
use Illuminate\View\Factory;
use Illuminate\View\FileViewFinder;
class IdeHelperServiceProvider extends ServiceProvider implements DeferrableProvider
{
@@ -64,41 +60,14 @@ class IdeHelperServiceProvider extends ServiceProvider implements DeferrableProv
{
$configPath = __DIR__ . '/../config/ide-helper.php';
$this->mergeConfigFrom($configPath, 'ide-helper');
$localViewFactory = $this->createLocalViewFactory();
$this->app->singleton(
'command.ide-helper.generate',
function ($app) use ($localViewFactory) {
return new GeneratorCommand($app['config'], $app['files'], $localViewFactory);
}
);
$this->app->singleton(
'command.ide-helper.models',
function ($app) {
return new ModelsCommand($app['files']);
}
);
$this->app->singleton(
'command.ide-helper.meta',
function ($app) use ($localViewFactory) {
return new MetaCommand($app['files'], $localViewFactory, $app['config']);
}
);
$this->app->singleton(
'command.ide-helper.eloquent',
function ($app) {
return new EloquentCommand($app['files']);
}
);
$this->commands(
'command.ide-helper.generate',
'command.ide-helper.models',
'command.ide-helper.meta',
'command.ide-helper.eloquent'
[
GeneratorCommand::class,
ModelsCommand::class,
MetaCommand::class,
EloquentCommand::class,
]
);
}
@@ -109,22 +78,11 @@ class IdeHelperServiceProvider extends ServiceProvider implements DeferrableProv
*/
public function provides()
{
return ['command.ide-helper.generate', 'command.ide-helper.models'];
}
/**
* @return Factory
*/
private function createLocalViewFactory()
{
$resolver = new EngineResolver();
$resolver->register('php', function () {
return new PhpEngine($this->app['files']);
});
$finder = new FileViewFinder($this->app['files'], [__DIR__ . '/../resources/views']);
$factory = new Factory($resolver, $finder, $this->app['events']);
$factory->addExtension('php', 'php');
return $factory;
return [
GeneratorCommand::class,
ModelsCommand::class,
MetaCommand::class,
EloquentCommand::class,
];
}
}
+54 -11
View File
@@ -9,27 +9,33 @@ use Illuminate\Support\Collection;
class Macro extends Method
{
protected static $macroDefaults = [];
/**
* Macro constructor.
*
* @param \ReflectionFunctionAbstract $method
* @param string $alias
* @param \ReflectionClass $class
* @param null $methodName
* @param array $interfaces
* @param array $classAliases
* @param array $returnTypeNormalizers
*/
public function __construct(
$method,
$alias,
$class,
$methodName = null,
$interfaces = [],
$classAliases = []
$classAliases = [],
$returnTypeNormalizers = []
) {
parent::__construct($method, $alias, $class, $methodName, $interfaces, $classAliases);
parent::__construct($method, $class, $methodName, $interfaces, $classAliases, $returnTypeNormalizers);
}
public static function setDefaultReturnTypes(array $map = [])
{
static::$macroDefaults = array_merge(static::$macroDefaults, $map);
}
/**
* @param \ReflectionFunctionAbstract $method
*/
@@ -67,29 +73,66 @@ class Macro extends Method
$type = $this->concatReflectionTypes($return);
/** @psalm-suppress UndefinedClass */
if (!$return instanceof \ReflectionUnionType) {
/** @phpstan-ignore method.notFound */
$type .= $this->root === "\\{$builder}" && $return->getName() === $builder ? '|static' : '';
$type .= $return->allowsNull() ? '|null' : '';
}
$this->phpdoc->appendTag(Tag::createInstance("@return {$type}"));
}
$class = ltrim($this->declaringClassName, '\\');
if (!$this->phpdoc->hasTag('return') && isset(static::$macroDefaults[$class])) {
$type = static::$macroDefaults[$class];
$this->phpdoc->appendTag(Tag::createInstance("@return {$type}"));
}
}
protected function concatReflectionTypes(?\ReflectionType $type): string
{
/** @psalm-suppress UndefinedClass */
$returnTypes = $type instanceof \ReflectionUnionType
? $type->getTypes()
: [$type];
if ($type instanceof \ReflectionNamedType) {
return $type->getName();
}
return Collection::make($returnTypes)
if ($type instanceof \ReflectionIntersectionType) {
return $this->formatIntersectionType($type);
}
if ($type instanceof \ReflectionUnionType) {
return $this->formatUnionType($type);
}
// Unknown or null type
return '';
}
protected function formatUnionType(\ReflectionUnionType $type): string
{
return Collection::make($type->getTypes())
->map(function (\ReflectionType $inner) {
if ($inner instanceof \ReflectionNamedType) {
return $inner->getName();
}
if ($inner instanceof \ReflectionIntersectionType) {
return $this->formatIntersectionType($inner);
}
// ReflectionUnionType cannot be nested per PHP's DNF rules
return null;
})
->filter()
->map->getName()
->implode('|');
}
protected function formatIntersectionType(\ReflectionIntersectionType $type): string
{
$parts = Collection::make($type->getTypes())
->map(fn (\ReflectionNamedType $t) => $t->getName())
->toArray();
return '(' . implode('&', $parts) . ')';
}
protected function addLocationToPhpDoc()
{
if ($this->method->name === '__invoke') {
+89 -19
View File
@@ -17,8 +17,6 @@ use Barryvdh\Reflection\DocBlock\Serializer as DocBlockSerializer;
use Barryvdh\Reflection\DocBlock\Tag;
use Barryvdh\Reflection\DocBlock\Tag\ParamTag;
use Barryvdh\Reflection\DocBlock\Tag\ReturnTag;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Str;
class Method
{
@@ -39,22 +37,29 @@ class Method
protected $return = null;
protected $root;
protected $classAliases;
protected $returnTypeNormalizers;
/** @var string[] */
protected $templateNames = [];
/**
* @param \ReflectionMethod|\ReflectionFunctionAbstract $method
* @param string $alias
* @param \ReflectionClass $class
* @param string|null $methodName
* @param array $interfaces
* @param array $classAliases
* @param array $returnTypeNormalizers
* @param string[] $templateNames
*/
public function __construct($method, $alias, $class, $methodName = null, $interfaces = [], array $classAliases = [])
public function __construct($method, $class, $methodName = null, $interfaces = [], array $classAliases = [], array $returnTypeNormalizers = [], array $templateNames = [])
{
$this->method = $method;
$this->interfaces = $interfaces;
$this->classAliases = $classAliases;
$this->returnTypeNormalizers = $returnTypeNormalizers;
$this->name = $methodName ?: $method->name;
$this->real_name = $method->isClosure() ? $this->name : $method->name;
$this->templateNames = $templateNames;
$this->initClassDefinedProperties($method, $class);
//Reference the 'real' function in the declaring class
@@ -71,6 +76,8 @@ class Method
} catch (\Exception $e) {
}
$this->removePhpDocParamTagsForOptionalParameters();
//Get the parameters, including formatted default values
$this->getParameters($method);
@@ -83,7 +90,7 @@ class Method
*/
protected function initPhpDoc($method)
{
$this->phpdoc = new DocBlock($method, new Context($this->namespace, $this->classAliases));
$this->phpdoc = new DocBlock($method, new Context($this->namespace, $this->classAliases, generics: $this->templateNames));
}
/**
@@ -172,7 +179,7 @@ class Method
/**
* Get the parameters for this method
*
* @param bool $implode Wether to implode the array or not
* @param bool $implode Whether to implode the array or not
* @return string
*/
public function getParams($implode = true)
@@ -180,10 +187,29 @@ class Method
return $implode ? implode(', ', $this->params) : $this->params;
}
/**
* @param DocBlock|null $phpdoc
* @return ReturnTag|null
*/
public function getReturnTag($phpdoc = null)
{
if ($phpdoc === null) {
$phpdoc = $this->phpdoc;
}
$returnTags = $phpdoc->getTagsByName('return');
if (count($returnTags) === 0) {
return null;
}
return reset($returnTags);
}
/**
* Get the parameters for this method including default values
*
* @param bool $implode Wether to implode the array or not
* @param bool $implode Whether to implode the array or not
* @return string
*/
public function getParamsWithDefault($implode = true)
@@ -224,6 +250,50 @@ class Method
}
}
/**
* Remove @param tags for optional reflection parameters so generated stubs match arity
* checks in static analysis (optional params still appear in the PHP signature).
*/
protected function removePhpDocParamTagsForOptionalParameters(): void
{
if (!$this->method instanceof \ReflectionMethod) {
return;
}
$declaring = $this->method->getDeclaringClass()->getName();
if (
$declaring !== \Illuminate\Database\Query\Builder::class
&& $declaring !== \Illuminate\Database\Eloquent\Builder::class
) {
return;
}
foreach ($this->method->getParameters() as $param) {
if (!$param->isOptional() || $param->isVariadic()) {
continue;
}
if (!$param->isDefaultValueAvailable()) {
continue;
}
$default = $param->getDefaultValue();
// Keep @param for null/array defaults (nullable and list defaults stay documented).
// Strip only non-null scalars (e.g. $boolean = 'and', $not = false) so tools that
// count @param tags do not report false "missing argument" errors on shortened calls.
if ($default === null || is_array($default) || !is_scalar($default)) {
continue;
}
$varName = '$' . $param->getName();
$paramTags = $this->phpdoc->getTagsByName('param');
foreach (array_values($paramTags) as $tag) {
if ($tag instanceof ParamTag && $tag->getVariableName() === $varName) {
$this->phpdoc->deleteTag($tag);
}
}
}
}
/**
* Normalize the parameters
*
@@ -248,25 +318,31 @@ class Method
}
/**
* Normalize the return tag (make full namespace, replace interfaces)
* Normalize the return tag (make full namespace, replace interfaces, resolve $this)
*
* @param DocBlock $phpdoc
*/
protected function normalizeReturn(DocBlock $phpdoc)
{
//Get the return type and adjust them for better autocomplete
$returnTags = $phpdoc->getTagsByName('return');
$tag = $this->getReturnTag($phpdoc);
if (count($returnTags) === 0) {
if ($tag === null) {
$this->return = null;
return;
}
/** @var ReturnTag $tag */
$tag = reset($returnTags);
// Get the expanded type
$returnValue = $tag->getType();
if (array_key_exists($returnValue, $this->returnTypeNormalizers)) {
$returnValue = $this->returnTypeNormalizers[$returnValue];
}
if ($returnValue === '$this') {
$returnValue = $this->root;
}
// Replace the interfaces
foreach ($this->interfaces as $interface => $real) {
$returnValue = str_replace($interface, $real, $returnValue);
@@ -275,12 +351,6 @@ class Method
// Set the changed content
$tag->setContent($returnValue . ' ' . $tag->getDescription());
$this->return = $returnValue;
if ($tag->getType() === '$this') {
Str::contains($this->root, Builder::class)
? $tag->setType($this->root . '|static')
: $tag->setType($this->root);
}
}
/**
@@ -366,7 +436,7 @@ class Method
}
if ($method) {
$namespace = $method->getDeclaringClass()->getNamespaceName();
$phpdoc = new DocBlock($method, new Context($namespace, $this->classAliases));
$phpdoc = new DocBlock($method, new Context($namespace, $this->classAliases, generics: $this->templateNames));
if (strpos($phpdoc->getText(), '{@inheritdoc}') !== false) {
//Not at the end yet, try another parent/interface..
-124
View File
@@ -1,124 +0,0 @@
<?php
/**
* Laravel IDE Helper Generator
*
* @author Barry vd. Heuvel <barryvdh@gmail.com>
* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl)
* @license http://www.opensource.org/licenses/mit-license.php MIT
* @link https://github.com/barryvdh/laravel-ide-helper
*/
namespace Barryvdh\LaravelIdeHelper;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use PhpParser\ParserFactory;
class UsesResolver
{
/**
* @param string $classFQN
* @return array
*/
public function loadFromClass(string $classFQN): array
{
return $this->loadFromFile(
$classFQN,
(new \ReflectionClass($classFQN))->getFileName()
);
}
/**
* @param string $classFQN
* @param string $filename
* @return array
*/
public function loadFromFile(string $classFQN, string $filename): array
{
return $this->loadFromCode(
$classFQN,
file_get_contents(
$filename
)
);
}
/**
* @param string $classFQN
* @param string $code
* @return array
*/
public function loadFromCode(string $classFQN, string $code): array
{
$classFQN = ltrim($classFQN, '\\');
$namespace = rtrim(
preg_replace(
'/([^\\\\]+)$/',
'',
$classFQN
),
'\\'
);
$parser = (new ParserFactory())->createForHostVersion();
$namespaceData = null;
foreach ($parser->parse($code) as $node) {
if ($node instanceof Namespace_ && $node->name->toCodeString() === $namespace) {
$namespaceData = $node;
break;
}
}
if ($namespaceData === null) {
return [];
}
/** @var Namespace_ $namespaceData */
$aliases = [];
foreach ($namespaceData->stmts as $stmt) {
if ($stmt instanceof Use_) {
if ($stmt->type !== Use_::TYPE_NORMAL) {
continue;
}
foreach ($stmt->uses as $use) {
/** @var UseUse $use */
$alias = $use->alias ?
$use->alias->name :
self::classBasename($use->name->toCodeString());
$aliases[$alias] = '\\' . $use->name->toCodeString();
}
} elseif ($stmt instanceof GroupUse) {
foreach ($stmt->uses as $use) {
/** @var UseUse $use */
$alias = $use->alias ?
$use->alias->name :
self::classBasename($use->name->toCodeString());
$aliases[$alias] = '\\' . $stmt->prefix->toCodeString() . '\\' . $use->name->toCodeString();
}
}
}
return $aliases;
}
/**
* @param string $classFQN
* @return string
*/
protected static function classBasename(string $classFQN): string
{
return preg_replace('/^.*\\\\([^\\\\]+)$/', '$1', $classFQN);
}
}
+55
View File
@@ -9,6 +9,7 @@ use Barryvdh\LaravelIdeHelper\Macro;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Facade;
/**
* @internal
@@ -66,6 +67,37 @@ class AliasTest extends TestCase
$this->assertNotNull($this->getAliasMacro($alias, EloquentBuilder::class, $macro));
}
/**
* @covers ::detectFake
*/
public function testNoExceptionOnRequiredFakeParameters(): void
{
// Mock
$alias = new AliasMock();
// Prepare
$alias->setFacade(MockFacade::class);
$this->expectNotToPerformAssertions();
// Test
$alias->detectFake();
}
/**
* @covers ::detectTemplateNames
*/
public function testTemplateNamesAreDetected(): void
{
// Mock
$alias = new AliasMock();
// Prepare
$alias->setClasses([EloquentBuilder::class]);
// Test
$this->assertSame(['TModel', 'TValue'], $alias->getTemplateNames());
}
protected function getAliasMacro(Alias $alias, string $class, string $method): ?Macro
{
return Arr::first(
@@ -82,6 +114,7 @@ class AliasTest extends TestCase
/**
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
* @template TValue
*/
class AliasMock extends Alias
{
@@ -102,4 +135,26 @@ class AliasMock extends Alias
{
parent::detectMethods();
}
public function detectFake()
{
parent::detectFake();
}
public function setFacade(string $facade)
{
$this->facade = $facade;
}
}
class MockFacade extends Facade
{
protected static function getFacadeAccessor()
{
return 'Illuminate\Foundation\Exceptions\Handler';
}
public static function fake(string $test1, $test2 = null)
{
}
}
+2 -2
View File
@@ -2,7 +2,7 @@
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console;
namespace Barryvdh\LaravelIdeHelper\Tests\Console\EloquentCommand;
use Barryvdh\LaravelIdeHelper\Console\EloquentCommand;
use Barryvdh\LaravelIdeHelper\Tests\TestCase;
@@ -23,7 +23,7 @@ class EloquentCommandTest extends TestCase
}
$actualContent = null;
$mockFilesystem = Mockery::mock(Filesystem::class);
$mockFilesystem = Mockery::mock(Filesystem::class)->makePartial();
$mockFilesystem
->shouldReceive('get')
// We don't care about actual args (filename)
@@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\GeneratorCommand;
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
use Barryvdh\LaravelIdeHelper\Tests\TestCase;
abstract class AbstractGeneratorCommand extends TestCase
{
protected function setUp(): void
{
parent::setUp();
$this->mockFilesystem();
}
/**
* Get package providers.
*
* @param \Illuminate\Foundation\Application $app
*
* @return array
*/
protected function getPackageProviders($app)
{
return [IdeHelperServiceProvider::class];
}
}
@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\GeneratorCommand\GenerateEloquentOnly;
use Barryvdh\LaravelIdeHelper\Console\GeneratorCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\GeneratorCommand\AbstractGeneratorCommand;
class Test extends AbstractGeneratorCommand
{
public function testGenerator(): void
{
$command = $this->app->make(GeneratorCommand::class);
$tester = $this->runCommand($command, [
'--eloquent' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('A new helper file was written to _ide_helper.php', $tester->getDisplay());
$this->assertStringNotContainsString('public static function configure($basePath = null)', $this->mockFilesystemOutput);
$this->assertStringContainsString('class Eloquent extends \Illuminate\Database\Eloquent\Model', $this->mockFilesystemOutput);
}
}
@@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\GeneratorCommand\GenerateIdeHelper;
use Barryvdh\LaravelIdeHelper\Console\GeneratorCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\GeneratorCommand\AbstractGeneratorCommand;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
class Test extends AbstractGeneratorCommand
{
public function testGenerator(): void
{
Arr::macro('arr_custom_macro', function () {
});
DB::macro('db_custom_macro', function () {
});
$this->app['config']->set('ide-helper.macro_default_return_types', [Arr::class => 'Custom_Fake_Class']);
$command = $this->app->make(GeneratorCommand::class);
$tester = $this->runCommand($command);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('A new helper file was written to _ide_helper.php', $tester->getDisplay());
$this->assertStringContainsString('public static function configure($basePath = null)', $this->mockFilesystemOutput);
$this->assertStringContainsString('* @return \Custom_Fake_Class', $this->mockFilesystemOutput);
$this->assertStringContainsString('public static function arr_custom_macro()', $this->mockFilesystemOutput);
$this->assertStringContainsString('public static function db_custom_macro()', $this->mockFilesystemOutput);
}
public function testFilename(): void
{
$command = $this->app->make(GeneratorCommand::class);
$tester = $this->runCommand($command, [
'filename' => 'foo.php',
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('A new helper file was written to foo.php', $tester->getDisplay());
}
}
+11 -8
View File
@@ -12,17 +12,22 @@ use stdClass;
class MetaCommandTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
$this->mockFilesystem();
}
public function testCommand(): void
{
$this->mockFilesystem();
/** @var Filesystem|MockInterface $mockFileSystem */
$mockFileSystem = $this->app->make(Filesystem::class);
$this->instance('files', $mockFileSystem);
$this->instance(Filesystem::class, $mockFileSystem);
$mockFileSystem
->shouldReceive('getRequire')
->andReturnUsing(function ($__path, $__data) {
->andReturnUsing(function ($__path, $__data = []) {
return (static function () use ($__path, $__data) {
extract($__data, EXTR_SKIP);
@@ -49,15 +54,13 @@ class MetaCommandTest extends TestCase
return new stdClass();
});
$this->mockFilesystem();
/** @var Filesystem|MockInterface $mockFileSystem */
$mockFileSystem = $this->app->make(Filesystem::class);
$this->instance('files', $mockFileSystem);
$this->instance(Filesystem::class, $mockFileSystem);
$mockFileSystem
->shouldReceive('getRequire')
->andReturnUsing(function ($__path, $__data) {
->andReturnUsing(function ($__path, $__data = []) {
return (static function () use ($__path, $__data) {
extract($__data, EXTR_SKIP);
@@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
use Barryvdh\LaravelIdeHelper\Tests\SnapshotPhpDriver;
use Barryvdh\LaravelIdeHelper\Tests\TestCase;
abstract class AbstractModelsCommand extends TestCase
@@ -51,10 +50,7 @@ abstract class AbstractModelsCommand extends TestCase
// Don't override integer -> int for tests
$config->set('ide-helper.type_overrides', []);
}
protected function assertMatchesMockedSnapshot()
{
$this->assertMatchesSnapshot($this->mockFilesystemOutput, new SnapshotPhpDriver());
$config->set('ide-helper.write_model_relation_exists_properties', true);
}
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Collections;
use Illuminate\Support\Collection;
class AdvancedCastCollection extends Collection
{
}
@@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Collections;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
class AdvancedCastMap implements Arrayable, JsonSerializable
{
public function toArray(): array
{
return [];
}
public function jsonSerialize(): array
{
return $this->toArray();
}
}
@@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Enums;
enum AdvancedCastEnum
{
case apple;
case banana;
case orange;
}
@@ -4,6 +4,9 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Collections\AdvancedCastCollection;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Collections\AdvancedCastMap;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Enums\AdvancedCastEnum;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
@@ -11,21 +14,28 @@ 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_enum_collection' => AsEnumCollection::class,
'cast_to_as_array_object' => AsArrayObject::class,
];
protected function casts(): array
{
return [
'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_collection_of' => AsCollection::class . ':,' . AdvancedCastMap::class, // since 12.10
'cast_to_as_collection_using' => AsCollection::using(AdvancedCastCollection::class),
'cast_to_as_collection_using_and_map' => AsCollection::class . ':' . AdvancedCastCollection::class . ',' . AdvancedCastMap::class, // since 12.10
'cast_to_as_enum_collection' => AsEnumCollection::class,
'cast_to_as_enum_collection_of' => AsEnumCollection::of(AdvancedCastEnum::class),
'cast_to_as_array_object' => AsArrayObject::class,
];
}
}
@@ -4,14 +4,15 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Models;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Collections\AdvancedCastCollection;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Collections\AdvancedCastMap;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AdvancedCasts\Enums\AdvancedCastEnum;
use Illuminate\Database\Eloquent\Casts\AsArrayObject;
use Illuminate\Database\Eloquent\Casts\AsCollection;
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property \Illuminate\Support\Carbon $cast_to_date_serialization
* @property \Illuminate\Support\Carbon $cast_to_datetime_serialization
* @property \Illuminate\Support\Carbon $cast_to_custom_datetime
@@ -19,14 +20,18 @@ use Illuminate\Database\Eloquent\Model;
* @property \Carbon\CarbonImmutable $cast_to_immutable_custom_datetime
* @property \Carbon\CarbonImmutable $cast_to_immutable_datetime
* @property int $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 string $cast_to_encrypted
* @property array<array-key, mixed> $cast_to_encrypted_array
* @property \Illuminate\Support\Collection<array-key, mixed> $cast_to_encrypted_collection
* @property array<array-key, mixed> $cast_to_encrypted_json
* @property object $cast_to_encrypted_object
* @property \Illuminate\Support\Collection $cast_to_as_collection
* @property \Illuminate\Support\Collection $cast_to_as_enum_collection
* @property \ArrayObject $cast_to_as_array_object
* @property \Illuminate\Database\Eloquent\Casts\ArrayObject<array-key, mixed> $cast_to_as_array_object
* @property \Illuminate\Support\Collection<int, AdvancedCastMap> $cast_to_as_collection_of
* @property AdvancedCastCollection $cast_to_as_collection_using
* @property AdvancedCastCollection<int, AdvancedCastMap> $cast_to_as_collection_using_and_map
* @property \Illuminate\Support\Collection<int, AdvancedCastEnum> $cast_to_as_enum_collection_of
* @method static \Illuminate\Database\Eloquent\Builder<static>|AdvancedCast newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|AdvancedCast newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|AdvancedCast query()
@@ -49,21 +54,28 @@ 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_enum_collection' => AsEnumCollection::class,
'cast_to_as_array_object' => AsArrayObject::class,
];
protected function casts(): array
{
return [
'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_collection_of' => AsCollection::class . ':,' . AdvancedCastMap::class, // since 12.10
'cast_to_as_collection_using' => AsCollection::using(AdvancedCastCollection::class),
'cast_to_as_collection_using_and_map' => AsCollection::class . ':' . AdvancedCastCollection::class . ',' . AdvancedCastMap::class, // since 12.10
'cast_to_as_enum_collection' => AsEnumCollection::class,
'cast_to_as_enum_collection_of' => AsEnumCollection::of(AdvancedCastEnum::class),
'cast_to_as_array_object' => AsArrayObject::class,
];
}
}
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AllowGlobDirecto
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -44,8 +42,8 @@ use Illuminate\Database\Eloquent\Model;
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ArrayCastsWithCo
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property array<int, string>|null $cast_to_array -- These three should not be duplicated
* @property array<int, string> $cast_to_json some-description
* @property \Illuminate\Support\Collection<int, string> $cast_to_collection some-description
@@ -8,8 +8,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $name
* @property string|null $name_read
@@ -85,8 +83,6 @@ use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property int $diverging_type_hinted_get_and_set
* @property string|null $name
@@ -10,8 +10,6 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
*
*
* @property int $id
* @property string $both_same_name I'm a getter
* @property string $both_without_getter_comment
@@ -23,6 +21,7 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
* @property-read string $not_comment
* @property-read \Illuminate\Database\Eloquent\Collection<int, Simple> $relationHasMany HasMany relations.
* @property-read int|null $relation_has_many_count
* @property-read bool|null $relation_has_many_exists
* @property-read Simple|null $relationHasOne Others relations.
* @property-read Model|\Eloquent $relationMorphTo MorphTo relations.
* @property-write mixed $first_name Set the user's first name.
@@ -9,11 +9,10 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
*
*
* @property int $id
* @property-read SimpleCollection<int, Simple> $relationHasMany
* @property-read int|null $relation_has_many_count
* @property-read bool|null $relation_has_many_exists
* @method static SimpleCollection<int, static> all($columns = ['*'])
* @method static SimpleCollection<int, static> get($columns = ['*'])
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newModelQuery()
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\CustomDate\Model
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property \Carbon\CarbonImmutable|null $created_at
* @property \Carbon\CarbonImmutable|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|CustomDate newModelQuery()
@@ -0,0 +1,218 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DnfTypes\Models;
use ArrayAccess;
use Countable;
use Illuminate\Database\Eloquent\Model;
use Iterator;
use Stringable;
use Traversable;
/**
* Test model covering all DNF type scenarios from PHP RFC.
*
* @see https://wiki.php.net/rfc/dnf_types
*/
class DnfTypeModel extends Model
{
// =========================================================================
// RFC Example 1: (A&B)|null - Intersection with null
// =========================================================================
/**
* Basic DNF: intersection OR null.
*/
public function getBasicDnfAttribute(): (Countable&Iterator)|null
{
return null;
}
// =========================================================================
// RFC Example 2: (A&B)|D - Intersection OR single type (no null)
// =========================================================================
/**
* Intersection OR single class type.
*/
public function getIntersectionOrClassAttribute(): (Countable&Iterator)|\stdClass
{
return new \stdClass();
}
// =========================================================================
// RFC Example 3: C|(X&D)|null - Single type OR intersection OR null
// =========================================================================
/**
* Single type OR intersection OR null.
*/
public function getSingleOrIntersectionOrNullAttribute(): \stdClass|(Countable&Iterator)|null
{
return null;
}
// =========================================================================
// RFC Example 4: (A&B&D)|int|null - Triple intersection OR primitive OR null
// =========================================================================
/**
* Triple intersection OR primitive OR null.
*/
public function getTripleIntersectionAttribute(): (Countable&Iterator&Traversable)|int|null
{
return null;
}
// =========================================================================
// RFC Example 5: (A&B)|(C&D)|null - Multiple intersection segments
// =========================================================================
/**
* Multiple intersection segments OR null.
*/
public function getMultipleIntersectionsAttribute(): (Countable&Iterator)|(ArrayAccess&Stringable)|null
{
return null;
}
// =========================================================================
// RFC Example 6: (A&B)|(C&D)|(E&F) - Multiple intersections without null
// =========================================================================
/**
* Multiple intersection segments without null.
*/
public function getMultipleIntersectionsNoNullAttribute(): (Countable&Iterator)|(ArrayAccess&Stringable)|(Traversable&Countable)
{
return new class () implements Countable, Iterator {
public function count(): int
{
return 0;
}
public function current(): mixed
{
return null;
}
public function key(): mixed
{
return null;
}
public function next(): void
{
}
public function rewind(): void
{
}
public function valid(): bool
{
return false;
}
};
}
// =========================================================================
// Pure intersection type (A&B) - No union, just intersection
// =========================================================================
/**
* Pure intersection type without union.
*/
public function getPureIntersectionAttribute(): Countable&Iterator
{
return new class () implements Countable, Iterator {
public function count(): int
{
return 0;
}
public function current(): mixed
{
return null;
}
public function key(): mixed
{
return null;
}
public function next(): void
{
}
public function rewind(): void
{
}
public function valid(): bool
{
return false;
}
};
}
// =========================================================================
// Parameter context: DNF types in method parameters
// =========================================================================
/**
* Scope with DNF parameter: (A&B)|null.
*/
public function scopeWithBasicDnfParam(
\Illuminate\Database\Query\Builder $query,
(Countable&Iterator)|null $param
): \Illuminate\Database\Query\Builder {
return $query;
}
/**
* Scope with complex DNF parameter: C|(A&B)|null.
*/
public function scopeWithComplexDnfParam(
\Illuminate\Database\Query\Builder $query,
\stdClass|(Countable&Iterator)|null $param
): \Illuminate\Database\Query\Builder {
return $query;
}
/**
* Scope with multiple intersection parameters.
*/
public function scopeWithMultipleIntersectionParam(
\Illuminate\Database\Query\Builder $query,
(Countable&Iterator)|(ArrayAccess&Stringable) $param
): \Illuminate\Database\Query\Builder {
return $query;
}
/**
* Scope with intersection OR primitive parameter.
*/
public function scopeWithIntersectionOrPrimitive(
\Illuminate\Database\Query\Builder $query,
(Countable&Iterator)|int $param
): \Illuminate\Database\Query\Builder {
return $query;
}
// =========================================================================
// Mixed: DNF in both parameter and return type
// =========================================================================
/**
* DNF in both parameter and return type.
*/
public function scopeWithDnfParamAndReturn(
\Illuminate\Database\Query\Builder $query,
(Countable&Iterator)|null $param
): (Countable&Iterator)|\Illuminate\Database\Query\Builder {
return $query;
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DnfTypes;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
'--write' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
}
@@ -0,0 +1,234 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DnfTypes\Models;
use ArrayAccess;
use Countable;
use Illuminate\Database\Eloquent\Model;
use Iterator;
use Stringable;
use Traversable;
/**
* Test model covering all DNF type scenarios from PHP RFC.
*
* @see https://wiki.php.net/rfc/dnf_types
* @property-read (\Countable&\Iterator)|null $basic_dnf
* @property-read (\Countable&\Iterator)|\stdClass $intersection_or_class
* @property-read (\Countable&\Iterator)|(\ArrayAccess&\Stringable)|null $multiple_intersections
* @property-read (\Countable&\Iterator)|(\ArrayAccess&\Stringable)|(\Traversable&\Countable) $multiple_intersections_no_null
* @property-read (\Countable&\Iterator) $pure_intersection
* @property-read \stdClass|(\Countable&\Iterator)|null $single_or_intersection_or_null
* @property-read (\Countable&\Iterator&\Traversable)|int|null $triple_intersection
* @method static \Illuminate\Database\Eloquent\Builder<static>|DnfTypeModel newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DnfTypeModel newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DnfTypeModel query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|DnfTypeModel withBasicDnfParam((\Countable&\Iterator)|null $param)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DnfTypeModel withComplexDnfParam(\stdClass|(\Countable&\Iterator)|null $param)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DnfTypeModel withDnfParamAndReturn((\Countable&\Iterator)|null $param)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DnfTypeModel withIntersectionOrPrimitive((\Countable&\Iterator)|int $param)
* @method static \Illuminate\Database\Eloquent\Builder<static>|DnfTypeModel withMultipleIntersectionParam((\Countable&\Iterator)|(\ArrayAccess&\Stringable) $param)
* @mixin \Eloquent
*/
class DnfTypeModel extends Model
{
// =========================================================================
// RFC Example 1: (A&B)|null - Intersection with null
// =========================================================================
/**
* Basic DNF: intersection OR null.
*/
public function getBasicDnfAttribute(): (Countable&Iterator)|null
{
return null;
}
// =========================================================================
// RFC Example 2: (A&B)|D - Intersection OR single type (no null)
// =========================================================================
/**
* Intersection OR single class type.
*/
public function getIntersectionOrClassAttribute(): (Countable&Iterator)|\stdClass
{
return new \stdClass();
}
// =========================================================================
// RFC Example 3: C|(X&D)|null - Single type OR intersection OR null
// =========================================================================
/**
* Single type OR intersection OR null.
*/
public function getSingleOrIntersectionOrNullAttribute(): \stdClass|(Countable&Iterator)|null
{
return null;
}
// =========================================================================
// RFC Example 4: (A&B&D)|int|null - Triple intersection OR primitive OR null
// =========================================================================
/**
* Triple intersection OR primitive OR null.
*/
public function getTripleIntersectionAttribute(): (Countable&Iterator&Traversable)|int|null
{
return null;
}
// =========================================================================
// RFC Example 5: (A&B)|(C&D)|null - Multiple intersection segments
// =========================================================================
/**
* Multiple intersection segments OR null.
*/
public function getMultipleIntersectionsAttribute(): (Countable&Iterator)|(ArrayAccess&Stringable)|null
{
return null;
}
// =========================================================================
// RFC Example 6: (A&B)|(C&D)|(E&F) - Multiple intersections without null
// =========================================================================
/**
* Multiple intersection segments without null.
*/
public function getMultipleIntersectionsNoNullAttribute(): (Countable&Iterator)|(ArrayAccess&Stringable)|(Traversable&Countable)
{
return new class () implements Countable, Iterator {
public function count(): int
{
return 0;
}
public function current(): mixed
{
return null;
}
public function key(): mixed
{
return null;
}
public function next(): void
{
}
public function rewind(): void
{
}
public function valid(): bool
{
return false;
}
};
}
// =========================================================================
// Pure intersection type (A&B) - No union, just intersection
// =========================================================================
/**
* Pure intersection type without union.
*/
public function getPureIntersectionAttribute(): Countable&Iterator
{
return new class () implements Countable, Iterator {
public function count(): int
{
return 0;
}
public function current(): mixed
{
return null;
}
public function key(): mixed
{
return null;
}
public function next(): void
{
}
public function rewind(): void
{
}
public function valid(): bool
{
return false;
}
};
}
// =========================================================================
// Parameter context: DNF types in method parameters
// =========================================================================
/**
* Scope with DNF parameter: (A&B)|null.
*/
public function scopeWithBasicDnfParam(
\Illuminate\Database\Query\Builder $query,
(Countable&Iterator)|null $param
): \Illuminate\Database\Query\Builder {
return $query;
}
/**
* Scope with complex DNF parameter: C|(A&B)|null.
*/
public function scopeWithComplexDnfParam(
\Illuminate\Database\Query\Builder $query,
\stdClass|(Countable&Iterator)|null $param
): \Illuminate\Database\Query\Builder {
return $query;
}
/**
* Scope with multiple intersection parameters.
*/
public function scopeWithMultipleIntersectionParam(
\Illuminate\Database\Query\Builder $query,
(Countable&Iterator)|(ArrayAccess&Stringable) $param
): \Illuminate\Database\Query\Builder {
return $query;
}
/**
* Scope with intersection OR primitive parameter.
*/
public function scopeWithIntersectionOrPrimitive(
\Illuminate\Database\Query\Builder $query,
(Countable&Iterator)|int $param
): \Illuminate\Database\Query\Builder {
return $query;
}
// =========================================================================
// Mixed: DNF in both parameter and return type
// =========================================================================
/**
* DNF in both parameter and return type.
*/
public function scopeWithDnfParamAndReturn(
\Illuminate\Database\Query\Builder $query,
(Countable&Iterator)|null $param
): (Countable&Iterator)|\Illuminate\Database\Query\Builder {
return $query;
}
}
@@ -13,8 +13,6 @@
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DoesNotGeneratePhpdocWithExternalEloquentBuilder\Models{
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -50,8 +48,8 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\DoesNotGenerateP
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -10,10 +10,9 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
/**
*
*
* @property-read \Illuminate\Database\Eloquent\Collection<int, Dynamic> $regularHasMany
* @property-read int|null $regular_has_many_count
* @property-read bool|null $regular_has_many_exists
* @method static \Illuminate\Database\Eloquent\Builder<static>|Dynamic newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Dynamic newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Dynamic query()
@@ -9,8 +9,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @method static \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\CustomSpace\ModelWithCustomNamespaceFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithCustomNamespace newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithCustomNamespace newQuery()
@@ -41,8 +39,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @method static \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories\ModelWithFactoryFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithFactory newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithFactory newQuery()
@@ -60,8 +56,6 @@ declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Models;
/**
*
*
* @method static \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Factories\Factories\ModelWithNestedFactoryFactory factory($count = null, $state = [])
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithNestedFactory newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithNestedFactory newQuery()
@@ -81,8 +75,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithoutFactory newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithoutFactory newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithoutFactory query()
@@ -9,8 +9,6 @@ use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -46,8 +44,8 @@ use Illuminate\Database\Eloquent\Model;
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -19,6 +19,8 @@ class Test extends AbstractModelsCommand
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertStringContainsString('Do you want to generate a minimal helper to generate the Eloquent methods?', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
}
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhp
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -44,8 +42,8 @@ use Illuminate\Database\Eloquent\Model;
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhp
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $charNullable
* @property string $charNotNullable
@@ -44,8 +42,8 @@ use Illuminate\Database\Eloquent\Model;
* @property float $floatNotNullable
* @property float|null $doubleNullable
* @property float $doubleNotNullable
* @property string|null $decimalNullable
* @property string $decimalNotNullable
* @property numeric|null $decimalNullable
* @property numeric $decimalNotNullable
* @property int|null $booleanNullable
* @property int $booleanNotNullable
* @property string|null $enumNullable
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhp
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -44,8 +42,8 @@ use Illuminate\Database\Eloquent\Model;
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpdocWithEloquentHelper\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
}
@@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpdocWithEloquentHelper;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
'--write' => true,
'--write-eloquent-helper' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertStringNotContainsString('Do you want to generate a minimal helper to generate the Eloquent methods?', $tester->getDisplay());
$this->assertStringContainsString('Eloquent helper was written to _ide_helper.php', $tester->getDisplay());
// $this->assertMatchesMockedSnapshot();
}
}
@@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateBasicPhpdocWithEloquentHelper\Models;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
* @property string|null $string_nullable
* @property string $string_not_nullable
* @property string|null $text_nullable
* @property string $text_not_nullable
* @property string|null $medium_text_nullable
* @property string $medium_text_not_nullable
* @property string|null $long_text_nullable
* @property string $long_text_not_nullable
* @property int|null $integer_nullable
* @property int $integer_not_nullable
* @property int|null $tiny_integer_nullable
* @property int $tiny_integer_not_nullable
* @property int|null $small_integer_nullable
* @property int $small_integer_not_nullable
* @property int|null $medium_integer_nullable
* @property int $medium_integer_not_nullable
* @property int|null $big_integer_nullable
* @property int $big_integer_not_nullable
* @property int|null $unsigned_integer_nullable
* @property int $unsigned_integer_not_nullable
* @property int|null $unsigned_tiny_integer_nullable
* @property int $unsigned_tiny_integer_not_nullable
* @property int|null $unsigned_small_integer_nullable
* @property int $unsigned_small_integer_not_nullable
* @property int|null $unsigned_medium_integer_nullable
* @property int $unsigned_medium_integer_not_nullable
* @property int|null $unsigned_big_integer_nullable
* @property int $unsigned_big_integer_not_nullable
* @property float|null $float_nullable
* @property float $float_not_nullable
* @property float|null $double_nullable
* @property float $double_not_nullable
* @property string|null $decimal_nullable
* @property string $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
* @property string $enum_not_nullable
* @property string|null $json_nullable
* @property string $json_not_nullable
* @property string|null $jsonb_nullable
* @property string $jsonb_not_nullable
* @property string|null $date_nullable
* @property string $date_not_nullable
* @property string|null $datetime_nullable
* @property string $datetime_not_nullable
* @property string|null $datetimetz_nullable
* @property string $datetimetz_not_nullable
* @property string|null $time_nullable
* @property string $time_not_nullable
* @property string|null $timetz_nullable
* @property string $timetz_not_nullable
* @property string|null $timestamp_nullable
* @property string $timestamp_not_nullable
* @property string|null $timestamptz_nullable
* @property string $timestamptz_not_nullable
* @property int|null $year_nullable
* @property int $year_not_nullable
* @property string|null $binary_nullable
* @property string $binary_not_nullable
* @property string|null $uuid_nullable
* @property string $uuid_not_nullable
* @property string|null $ipaddress_nullable
* @property string $ipaddress_not_nullable
* @property string|null $macaddress_nullable
* @property string $macaddress_not_nullable
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBigIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBigIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBinaryNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBinaryNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBooleanNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereBooleanNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereCharNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereCharNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDateNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDateNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDatetimeNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDatetimeNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDatetimetzNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDatetimetzNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDecimalNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDecimalNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDoubleNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereDoubleNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereEnumNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereEnumNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereFloatNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereFloatNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereIpaddressNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereIpaddressNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereJsonNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereJsonNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereJsonbNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereJsonbNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereLongTextNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereLongTextNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMacaddressNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMacaddressNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMediumIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMediumIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMediumTextNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereMediumTextNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereSmallIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereSmallIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereStringNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereStringNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTextNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTextNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimeNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimeNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimestampNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimestampNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimestamptzNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimestamptzNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimetzNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTimetzNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTinyIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereTinyIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedBigIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedBigIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedMediumIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedMediumIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedSmallIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedSmallIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedTinyIntegerNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUnsignedTinyIntegerNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUuidNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereUuidNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereYearNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Post whereYearNullable($value)
* @mixin \Eloquent
*/
class Post extends Model
{
}
@@ -67,8 +67,6 @@ class WithCollection extends Model
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateMixinCollection\Models{
/**
*
*
* @property-read \Illuminate\Support\Collection<int, string> $collection
* @property-read \Illuminate\Support\Collection<\Illuminate\Support\Collection, \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateMixinCollection\NonModels\CollectionModel<\Illuminate\Support\Collection, \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateMixinCollection\NonModels\CollectionModel<int, \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateMixinCollection\NonModels\NonModel>>> $collection_with_nested_template
* @property-read \Illuminate\Support\Collection<int, \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GenerateMixinCollection\NonModels\NonModel> $collection_with_non_model_template
@@ -0,0 +1,251 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpDocWithTypedScopeParameter\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
/**
* @comment Scope with required boolean parameter
*/
protected function scopeTyped01(Builder $query, bool $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional boolean parameter and default true
*/
protected function scopeTyped02(Builder $query, bool $value = true)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional boolean parameter and default false
*/
protected function scopeTyped03(Builder $query, bool $value = false)
{
$query->where('type', $value);
}
/**
* @comment Scope with required string parameter
*/
protected function scopeTyped04(Builder $query, string $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional string parameter and default value
*/
protected function scopeTyped05(Builder $query, string $value = 'dummy123')
{
$query->where('type', $value);
}
/**
* @comment Scope with required integer parameter
*/
protected function scopeTyped06(Builder $query, int $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional integer parameter and default positive value
*/
protected function scopeTyped07(Builder $query, int $value = 123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional integer parameter and default negative value
*/
protected function scopeTyped08(Builder $query, int $value = -123)
{
$query->where('type', $value);
}
/**
* @comment Scope with required float parameter
*/
protected function scopeTyped09(Builder $query, float $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default positive integer value
*/
protected function scopeTyped10(Builder $query, float $value = 123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default negative integer value
*/
protected function scopeTyped11(Builder $query, float $value = -123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default positive float value
*/
protected function scopeTyped12(Builder $query, float $value = 1.23)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default negative float value
*/
protected function scopeTyped13(Builder $query, float $value = -1.23)
{
$query->where('type', $value);
}
/**
* @comment Scope with required nullable boolean parameter
*/
protected function scopeTyped14(Builder $query, ?bool $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable boolean parameter and default true
*/
protected function scopeTyped15(Builder $query, ?bool $value = true)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable boolean parameter and default false
*/
protected function scopeTyped16(Builder $query, ?bool $value = false)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable boolean parameter and default null
*/
protected function scopeTyped17(Builder $query, ?bool $value = null)
{
$query->where('type', $value);
}
/**
* @comment Scope with required nullable string parameter
*/
protected function scopeTyped18(Builder $query, ?string $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable string parameter and default value
*/
protected function scopeTyped19(Builder $query, ?string $value = 'dummy123')
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable string parameter and default null
*/
protected function scopeTyped20(Builder $query, ?string $value = null)
{
$query->where('type', $value);
}
/**
* @comment Scope with required nullable integer parameter
*/
protected function scopeTyped21(Builder $query, ?int $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable integer parameter and default positive value
*/
protected function scopeTyped22(Builder $query, ?int $value = 123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable integer parameter and default negative value
*/
protected function scopeTyped23(Builder $query, ?int $value = -123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable integer parameter and default null
*/
protected function scopeTyped24(Builder $query, ?int $value = null)
{
$query->where('type', $value);
}
/**
* @comment Scope with required float nullable parameter
*/
protected function scopeTyped25(Builder $query, ?float $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable float parameter and default positive integer value
*/
protected function scopeTyped26(Builder $query, ?float $value = 123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable float parameter and default negative integer value
*/
protected function scopeTyped27(Builder $query, ?float $value = -123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default positive float value
*/
protected function scopeTyped28(Builder $query, ?float $value = 1.23)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default negative float value
*/
protected function scopeTyped29(Builder $query, ?float $value = -1.23)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default null
*/
protected function scopeTyped30(Builder $query, ?float $value = null)
{
$query->where('type', $value);
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpDocWithTypedScopeParameter;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
'--write' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
}
@@ -0,0 +1,287 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpDocWithTypedScopeParameter\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
/**
* @method static Builder<static>|Comment newModelQuery()
* @method static Builder<static>|Comment newQuery()
* @method static Builder<static>|Comment query()
* @method static Builder<static>|Comment typed01(bool $value) Scope with required boolean parameter
* @method static Builder<static>|Comment typed02(bool $value = true) Scope with optional boolean parameter and default true
* @method static Builder<static>|Comment typed03(bool $value = false) Scope with optional boolean parameter and default false
* @method static Builder<static>|Comment typed04(string $value) Scope with required string parameter
* @method static Builder<static>|Comment typed05(string $value = 'dummy123') Scope with optional string parameter and default value
* @method static Builder<static>|Comment typed06(int $value) Scope with required integer parameter
* @method static Builder<static>|Comment typed07(int $value = 123) Scope with optional integer parameter and default positive value
* @method static Builder<static>|Comment typed08(int $value = -123) Scope with optional integer parameter and default negative value
* @method static Builder<static>|Comment typed09(float $value) Scope with required float parameter
* @method static Builder<static>|Comment typed10(float $value = 123) Scope with optional float parameter and default positive integer value
* @method static Builder<static>|Comment typed11(float $value = -123) Scope with optional float parameter and default negative integer value
* @method static Builder<static>|Comment typed12(float $value = 1.23) Scope with optional float parameter and default positive float value
* @method static Builder<static>|Comment typed13(float $value = -1.23) Scope with optional float parameter and default negative float value
* @method static Builder<static>|Comment typed14(?bool $value) Scope with required nullable boolean parameter
* @method static Builder<static>|Comment typed15(?bool $value = true) Scope with optional nullable boolean parameter and default true
* @method static Builder<static>|Comment typed16(?bool $value = false) Scope with optional nullable boolean parameter and default false
* @method static Builder<static>|Comment typed17(?bool $value = null) Scope with optional nullable boolean parameter and default null
* @method static Builder<static>|Comment typed18(?string $value) Scope with required nullable string parameter
* @method static Builder<static>|Comment typed19(?string $value = 'dummy123') Scope with optional nullable string parameter and default value
* @method static Builder<static>|Comment typed20(?string $value = null) Scope with optional nullable string parameter and default null
* @method static Builder<static>|Comment typed21(?int $value) Scope with required nullable integer parameter
* @method static Builder<static>|Comment typed22(?int $value = 123) Scope with optional nullable integer parameter and default positive value
* @method static Builder<static>|Comment typed23(?int $value = -123) Scope with optional nullable integer parameter and default negative value
* @method static Builder<static>|Comment typed24(?int $value = null) Scope with optional nullable integer parameter and default null
* @method static Builder<static>|Comment typed25(?float $value) Scope with required float nullable parameter
* @method static Builder<static>|Comment typed26(?float $value = 123) Scope with optional nullable float parameter and default positive integer value
* @method static Builder<static>|Comment typed27(?float $value = -123) Scope with optional nullable float parameter and default negative integer value
* @method static Builder<static>|Comment typed28(?float $value = 1.23) Scope with optional float parameter and default positive float value
* @method static Builder<static>|Comment typed29(?float $value = -1.23) Scope with optional float parameter and default negative float value
* @method static Builder<static>|Comment typed30(?float $value = null) Scope with optional float parameter and default null
* @mixin \Eloquent
*/
class Comment extends Model
{
/**
* @comment Scope with required boolean parameter
*/
protected function scopeTyped01(Builder $query, bool $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional boolean parameter and default true
*/
protected function scopeTyped02(Builder $query, bool $value = true)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional boolean parameter and default false
*/
protected function scopeTyped03(Builder $query, bool $value = false)
{
$query->where('type', $value);
}
/**
* @comment Scope with required string parameter
*/
protected function scopeTyped04(Builder $query, string $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional string parameter and default value
*/
protected function scopeTyped05(Builder $query, string $value = 'dummy123')
{
$query->where('type', $value);
}
/**
* @comment Scope with required integer parameter
*/
protected function scopeTyped06(Builder $query, int $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional integer parameter and default positive value
*/
protected function scopeTyped07(Builder $query, int $value = 123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional integer parameter and default negative value
*/
protected function scopeTyped08(Builder $query, int $value = -123)
{
$query->where('type', $value);
}
/**
* @comment Scope with required float parameter
*/
protected function scopeTyped09(Builder $query, float $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default positive integer value
*/
protected function scopeTyped10(Builder $query, float $value = 123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default negative integer value
*/
protected function scopeTyped11(Builder $query, float $value = -123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default positive float value
*/
protected function scopeTyped12(Builder $query, float $value = 1.23)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default negative float value
*/
protected function scopeTyped13(Builder $query, float $value = -1.23)
{
$query->where('type', $value);
}
/**
* @comment Scope with required nullable boolean parameter
*/
protected function scopeTyped14(Builder $query, ?bool $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable boolean parameter and default true
*/
protected function scopeTyped15(Builder $query, ?bool $value = true)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable boolean parameter and default false
*/
protected function scopeTyped16(Builder $query, ?bool $value = false)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable boolean parameter and default null
*/
protected function scopeTyped17(Builder $query, ?bool $value = null)
{
$query->where('type', $value);
}
/**
* @comment Scope with required nullable string parameter
*/
protected function scopeTyped18(Builder $query, ?string $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable string parameter and default value
*/
protected function scopeTyped19(Builder $query, ?string $value = 'dummy123')
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable string parameter and default null
*/
protected function scopeTyped20(Builder $query, ?string $value = null)
{
$query->where('type', $value);
}
/**
* @comment Scope with required nullable integer parameter
*/
protected function scopeTyped21(Builder $query, ?int $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable integer parameter and default positive value
*/
protected function scopeTyped22(Builder $query, ?int $value = 123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable integer parameter and default negative value
*/
protected function scopeTyped23(Builder $query, ?int $value = -123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable integer parameter and default null
*/
protected function scopeTyped24(Builder $query, ?int $value = null)
{
$query->where('type', $value);
}
/**
* @comment Scope with required float nullable parameter
*/
protected function scopeTyped25(Builder $query, ?float $value)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable float parameter and default positive integer value
*/
protected function scopeTyped26(Builder $query, ?float $value = 123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional nullable float parameter and default negative integer value
*/
protected function scopeTyped27(Builder $query, ?float $value = -123)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default positive float value
*/
protected function scopeTyped28(Builder $query, ?float $value = 1.23)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default negative float value
*/
protected function scopeTyped29(Builder $query, ?float $value = -1.23)
{
$query->where('type', $value);
}
/**
* @comment Scope with optional float parameter and default null
*/
protected function scopeTyped30(Builder $query, ?float $value = null)
{
$query->where('type', $value);
}
}
@@ -13,8 +13,6 @@
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithExternalEloquentBuilder\Models{
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -50,8 +48,8 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWi
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -8,8 +8,6 @@ use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithExte
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -45,8 +43,8 @@ use Illuminate\Database\Eloquent\Model;
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -9,8 +9,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -46,8 +44,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -84,6 +82,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \Illuminate\Support\Carbon|null $updated_at
* @property-read \Illuminate\Database\Eloquent\Collection<int, \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post> $posts
* @property-read int|null $posts_count
* @property-read bool|null $posts_exists
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post null(string $unusedParam)
@@ -160,7 +159,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post whereUuidNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post whereYearNotNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post whereYearNullable($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post withTrashed()
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post withTrashed(bool $withTrashed = true)
* @method static \Illuminate\Database\Eloquent\Builder<static>|\Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithForcedFqn\Models\Post withoutTrashed()
* @mixin \Eloquent
*/
@@ -15,8 +15,6 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Support\Carbon;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property CastType $char_not_nullable
@@ -52,8 +50,8 @@ use Illuminate\Support\Carbon;
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -90,6 +88,7 @@ use Illuminate\Support\Carbon;
* @property Carbon|null $updated_at
* @property-read Collection<int, Post> $posts
* @property-read int|null $posts_count
* @property-read bool|null $posts_exists
* @method static EloquentBuilder<static>|Post newModelQuery()
* @method static EloquentBuilder<static>|Post newQuery()
* @method static EloquentBuilder<static>|Post null(string $unusedParam)
@@ -166,7 +165,7 @@ use Illuminate\Support\Carbon;
* @method static EloquentBuilder<static>|Post whereUuidNullable($value)
* @method static EloquentBuilder<static>|Post whereYearNotNullable($value)
* @method static EloquentBuilder<static>|Post whereYearNullable($value)
* @method static EloquentBuilder<static>|Post withTrashed()
* @method static EloquentBuilder<static>|Post withTrashed(bool $withTrashed = true)
* @method static EloquentBuilder<static>|Post withoutTrashed()
* @mixin Eloquent
*/
@@ -13,8 +13,6 @@
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithFqnInExternalFile\Models{
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -50,8 +48,8 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWi
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -45,8 +45,6 @@ class Post extends Model
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models{
/**
*
*
* @property $someProp
* @method someMethod(string $method)
* @method static \Illuminate\Database\Eloquent\Builder<static>|FinalPost newModelQuery()
@@ -60,8 +58,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWi
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWithMixin\Models{
/**
*
*
* @property $someProp
* @method someMethod(string $method)
* @property int $id
@@ -99,8 +95,8 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\GeneratePhpdocWi
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -9,13 +9,13 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
/**
*
*
* @property int $id
* @property-read \Illuminate\Database\Eloquent\Collection|Simple[] $regularBelongsToMany
* @property-read int|null $regular_belongs_to_many_count
* @property-read bool|null $regular_belongs_to_many_exists
* @property-read \Illuminate\Database\Eloquent\Collection|Simple[] $regularHasMany
* @property-read int|null $regular_has_many_count
* @property-read bool|null $regular_has_many_exists
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple query()
@@ -8,8 +8,6 @@ use DateTime;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property-read int|null $attribute_return_type_int_or_null
* @property-read array $attribute_returns_array
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Ignored\Models;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @method static \Illuminate\Database\Eloquent\Builder<static>|NotIgnored newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|NotIgnored newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|NotIgnored query()
@@ -13,8 +13,6 @@
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\Interfaces\Models{
/**
*
*
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
@@ -24,8 +24,6 @@ use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Cas
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_return_type
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_return_docblock
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_return_docblock_fqn
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MagicWhere\Model
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property string|null $char_nullable
* @property string $char_not_nullable
@@ -44,8 +42,8 @@ use Illuminate\Database\Eloquent\Model;
* @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 numeric|null $decimal_nullable
* @property numeric $decimal_not_nullable
* @property int|null $boolean_nullable
* @property int $boolean_not_nullable
* @property string|null $enum_nullable
@@ -9,8 +9,6 @@ use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks\CustomMethod;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks\CustomProperty;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Hooks\UnsetMethod;
use Illuminate\Filesystem\Filesystem;
use Mockery;
class Test extends AbstractModelsCommand
{
@@ -34,24 +32,6 @@ class Test extends AbstractModelsCommand
public function test(): void
{
$actualContent = null;
$mockFilesystem = Mockery::mock(Filesystem::class);
$mockFilesystem
->shouldReceive('get')
->andReturn(file_get_contents(__DIR__ . '/Models/Simple.php'))
->once();
$mockFilesystem
->shouldReceive('put')
->with(
Mockery::any(),
Mockery::capture($actualContent)
)
->andReturn(1) // Simulate we wrote _something_ to the file
->once();
$this->instance(Filesystem::class, $mockFilesystem);
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
@@ -60,33 +40,6 @@ class Test extends AbstractModelsCommand
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$expectedContent = <<<'PHP'
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Models;
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @property-read string $custom
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple custom($custom)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple whereId($value)
* @mixin \Eloquent
*/
class Simple extends Model
{
}
PHP;
$this->assertSame($expectedContent, $actualContent);
$this->assertMatchesMockedSnapshot();
}
}
@@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\ModelHooks\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property-read string $custom
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple custom($custom)
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple whereId($value)
* @mixin \Eloquent
*/
class Simple extends Model
{
}
@@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
use Illuminate\Database\Eloquent\Model;
abstract class BaseModel extends Model
{
}
@@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
interface CanBeAssigned
{
}
@@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class MorphToIntersection extends Model
{
protected $table = 'morphs';
/** @return MorphTo<(BaseModel&CanBeAssigned), $this> */
public function assigneeWithParens(): MorphTo
{
return $this->morphTo(type: 'nullable_relation_morph_to_type', id: 'nullable_relation_morph_to_id');
}
/** @return MorphTo<BaseModel&CanBeAssigned, $this> */
public function assigneeWithoutParens(): MorphTo
{
return $this->morphTo(type: 'nullable_relation_morph_to_type', id: 'nullable_relation_morph_to_id');
}
/** @return MorphTo<(BaseModel&CanBeAssigned), $this> */
public function nonNullableAssignee(): MorphTo
{
return $this->morphTo(type: 'relation_morph_to_type', id: 'relation_morph_to_id');
}
}
@@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand
{
public function test(): void
{
$command = $this->app->make(ModelsCommand::class);
$tester = $this->runCommand($command, [
'--write' => true,
]);
$this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertMatchesMockedSnapshot();
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\MorphToIntersection\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @property string $relation_morph_to_type
* @property int $relation_morph_to_id
* @property string|null $nullable_relation_morph_to_type
* @property int|null $nullable_relation_morph_to_id
* @property-read (BaseModel&CanBeAssigned)|null $assigneeWithParens
* @property-read (BaseModel&CanBeAssigned)|null $assigneeWithoutParens
* @property-read (BaseModel&CanBeAssigned) $nonNullableAssignee
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection whereNullableRelationMorphToId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection whereNullableRelationMorphToType($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection whereRelationMorphToId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|MorphToIntersection whereRelationMorphToType($value)
* @mixin \Eloquent
*/
class MorphToIntersection extends Model
{
protected $table = 'morphs';
/** @return MorphTo<(BaseModel&CanBeAssigned), $this> */
public function assigneeWithParens(): MorphTo
{
return $this->morphTo(type: 'nullable_relation_morph_to_type', id: 'nullable_relation_morph_to_id');
}
/** @return MorphTo<BaseModel&CanBeAssigned, $this> */
public function assigneeWithoutParens(): MorphTo
{
return $this->morphTo(type: 'nullable_relation_morph_to_type', id: 'nullable_relation_morph_to_id');
}
/** @return MorphTo<(BaseModel&CanBeAssigned), $this> */
public function nonNullableAssignee(): MorphTo
{
return $this->morphTo(type: 'relation_morph_to_type', id: 'relation_morph_to_id');
}
}
@@ -8,8 +8,6 @@ use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
*
*
* @property string $relation_morph_to_type
* @property int $relation_morph_to_id
* @property string|null $nullable_relation_morph_to_type
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PHPStormNoInspec
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newQuery()
@@ -7,8 +7,6 @@ namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PHPStormNoInspec
use Illuminate\Database\Eloquent\Model;
/**
*
*
* @property int $id
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|Simple newQuery()
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
use Illuminate\Database\Eloquent\Model;
// Tests: final class + attribute with nested array argument (e.g. ObservedBy([...]))
#[ObservedByStub([StubObserver::class])]
final class FinalWithNested extends Model
{
protected $table = 'simples';
}
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesBeforeClass\Models;
use Illuminate\Database\Eloquent\Model;
// Tests: multiple consecutive PHP 8 attributes before the class declaration
#[\AllowDynamicProperties]
#[SecondAttribute]
class MultipleAttributes extends Model
{
protected $table = 'simples';
}

Some files were not shown because too many files have changed in this diff Show More