Compare commits

..
Author SHA1 Message Date
copilot-swe-agent[bot] 60bf02fd19 Initial plan 2026-03-05 20:07:19 +00:00
22 changed files with 54 additions and 254 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- name: Git checkout - name: Git checkout
uses: actions/checkout@v7 uses: actions/checkout@v6
- name: Validate Composer configuration - name: Validate Composer configuration
run: composer validate --strict run: composer validate --strict
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v7 uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
# echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV # echo "GHE_HOST=${GITHUB_SERVER_URL##https:\/\/}" >> $GITHUB_ENV
# Drafts your next Release notes as Pull Requests are merged into "master" # Drafts your next Release notes as Pull Requests are merged into "master"
- uses: release-drafter/release-drafter@v7 - uses: release-drafter/release-drafter@v6
# (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml
# with: # with:
# config-name: my-config.yml # config-name: my-config.yml
+2 -2
View File
@@ -21,7 +21,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
php: [8.5, 8.4, 8.3, 8.2] php: [8.5, 8.4, 8.3, 8.2]
laravel: [12.x, 13.x] laravel: [11.x, 12.x, 13.x]
exclude: exclude:
- laravel: 13.x - laravel: 13.x
php: 8.2 php: 8.2
@@ -30,7 +30,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
path: src path: src
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
+1 -1
View File
@@ -41,7 +41,7 @@ jobs:
git config --global core.eol lf git config --global core.eol lf
- name: Checkout code - name: Checkout code
uses: actions/checkout@v7 uses: actions/checkout@v6
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v7 uses: actions/checkout@v6
with: with:
ref: ${{ github.event.release.target_commitish }} ref: ${{ github.event.release.target_commitish }}
-33
View File
@@ -1,38 +1,5 @@
# Changelog # Changelog
## v3.7.0 - 2026-03-17
### What's Changed
* Skip calling fake() when required parameters exist (Socialite compatibility) by @Jaspur in https://github.com/barryvdh/laravel-ide-helper/pull/1746
* Make Soft Deleted Relationships Nullable by @evan-burrell in https://github.com/barryvdh/laravel-ide-helper/pull/1749
* Support all DNF types from PHP RFC by @gurmanolog in https://github.com/barryvdh/laravel-ide-helper/pull/1751
* Replace psalm with larastan by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1755
* perf: reduce redundant lookups and file I/O in generation hot paths by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1757
* fix: wrap bare intersection types in parentheses when adding nullable by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1756
* feat(ModelsCommand): add configuration option to disable model query methods by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1692
* ci: add PHP 8.5 to integration test matrix by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1760
* chore: update test snapshots after merge order issue by @pataar in https://github.com/barryvdh/laravel-ide-helper/pull/1762
* Laravel 13.x Compatibility by @laravel-shift in https://github.com/barryvdh/laravel-ide-helper/pull/1763
* fix: Correct indentation in camel case config comment block by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1767
* fix: Place PHPDoc before class attributes when writing to models by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1765
* fix: Don't extend root's parent class for facade stubs in helper file by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1766
* Fix PHPDoc placement before PHP 8 class attributes and add regression tests by @Copilot in https://github.com/barryvdh/laravel-ide-helper/pull/1769
* fix: Skip autoload exception when class existence is being checked by @isaackaara in https://github.com/barryvdh/laravel-ide-helper/pull/1764
* Remove closure in attribute test by @barryvdh in https://github.com/barryvdh/laravel-ide-helper/pull/1772
* Bump branch alias to 3.6 by @jnoordsij in https://github.com/barryvdh/laravel-ide-helper/pull/1774
### New Contributors
* @Jaspur made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1746
* @evan-burrell made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1749
* @gurmanolog made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1751
* @laravel-shift made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1763
* @isaackaara made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1767
* @Copilot made their first contribution in https://github.com/barryvdh/laravel-ide-helper/pull/1769
**Full Changelog**: https://github.com/barryvdh/laravel-ide-helper/compare/v3.6.1...v3.7.0
## v3.6.1 - 2025-12-10 ## v3.6.1 - 2025-12-10
### What's Changed ### What's Changed
+1 -1
View File
@@ -66,7 +66,7 @@
}, },
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "3.7-dev" "dev-master": "3.5-dev"
}, },
"laravel": { "laravel": {
"providers": [ "providers": [
+1 -6
View File
@@ -101,12 +101,7 @@ class Alias
} }
if ($facade === '\Illuminate\Database\Eloquent\Model') { if ($facade === '\Illuminate\Database\Eloquent\Model') {
$this->usedMethods = [ $this->usedMethods = ['decrement' => true, 'increment' => true];
'decrement' => true,
'decrementEach' => true,
'increment' => true,
'incrementEach' => true,
];
} }
} }
+1 -44
View File
@@ -44,7 +44,6 @@ use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany; use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Database\Eloquent\Relations\Pivot; use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Database\Schema\Builder; use Illuminate\Database\Schema\Builder;
use Illuminate\Filesystem\Filesystem; use Illuminate\Filesystem\Filesystem;
use Illuminate\Support\Arr; use Illuminate\Support\Arr;
@@ -944,55 +943,13 @@ class ModelsCommand extends Command
} }
} }
if ( if ($this->relatedModelUsesSoftDeletes($relationObj)) {
$this->relatedModelUsesSoftDeletes($relationObj)
&& !$this->relationIncludesNonTrashedParents($relationObj)
) {
return true; return true;
} }
return false; return false;
} }
/**
* Check whether the relation explicitly opts into returning non-soft-deleted parents
* via ->withTrashed(), in which case the SoftDeletes-based nullability no longer applies.
*
* Returns false for ->onlyTrashed() and ->withoutTrashed(), which still leave the
* relation potentially empty depending on the parent's soft-delete state.
*
* @param Relation $relationObj
*
* @return bool
*/
protected function relationIncludesNonTrashedParents(Relation $relationObj): bool
{
$query = $relationObj->getQuery();
if (!in_array(SoftDeletingScope::class, $query->removedScopes(), true)) {
return false;
}
$relatedModel = $relationObj->getRelated();
if (!method_exists($relatedModel, 'getQualifiedDeletedAtColumn')) {
return true;
}
$deletedAtColumn = $relatedModel->getQualifiedDeletedAtColumn();
foreach ($query->getQuery()->wheres ?? [] as $where) {
if (
($where['column'] ?? null) === $deletedAtColumn
&& in_array($where['type'] ?? null, ['Null', 'NotNull'], true)
) {
return false;
}
}
return true;
}
/** /**
* Check if the related model uses the SoftDeletes trait * Check if the related model uses the SoftDeletes trait
* *
-46
View File
@@ -76,8 +76,6 @@ class Method
} catch (\Exception $e) { } catch (\Exception $e) {
} }
$this->removePhpDocParamTagsForOptionalParameters();
//Get the parameters, including formatted default values //Get the parameters, including formatted default values
$this->getParameters($method); $this->getParameters($method);
@@ -250,50 +248,6 @@ class Method
} }
} }
/**
* Remove @param tags for optional reflection parameters so generated stubs match arity
* checks in static analysis (optional params still appear in the PHP signature).
*/
protected function removePhpDocParamTagsForOptionalParameters(): void
{
if (!$this->method instanceof \ReflectionMethod) {
return;
}
$declaring = $this->method->getDeclaringClass()->getName();
if (
$declaring !== \Illuminate\Database\Query\Builder::class
&& $declaring !== \Illuminate\Database\Eloquent\Builder::class
) {
return;
}
foreach ($this->method->getParameters() as $param) {
if (!$param->isOptional() || $param->isVariadic()) {
continue;
}
if (!$param->isDefaultValueAvailable()) {
continue;
}
$default = $param->getDefaultValue();
// Keep @param for null/array defaults (nullable and list defaults stay documented).
// Strip only non-null scalars (e.g. $boolean = 'and', $not = false) so tools that
// count @param tags do not report false "missing argument" errors on shortened calls.
if ($default === null || is_array($default) || !is_scalar($default)) {
continue;
}
$varName = '$' . $param->getName();
$paramTags = $this->phpdoc->getTagsByName('param');
foreach (array_values($paramTags) as $tag) {
if ($tag instanceof ParamTag && $tag->getVariableName() === $varName) {
$this->phpdoc->deleteTag($tag);
}
}
}
}
/** /**
* Normalize the parameters * Normalize the parameters
* *
@@ -0,0 +1,14 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesClosureInAttribute\Models;
use Illuminate\Database\Eloquent\Model;
// Tests: closure expression inside an attribute argument (PHP 8.5+)
#[SomeClosureAttr(fn () => true)]
class ClosureInAttribute extends Model
{
protected $table = 'simples';
}
@@ -2,13 +2,16 @@
declare(strict_types=1); declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UniqueColumn; namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesClosureInAttribute;
use Barryvdh\LaravelIdeHelper\Console\ModelsCommand; use Barryvdh\LaravelIdeHelper\Console\ModelsCommand;
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand; use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\AbstractModelsCommand;
class Test extends AbstractModelsCommand class Test extends AbstractModelsCommand
{ {
/**
* @requires PHP 8.5
*/
public function test(): void public function test(): void
{ {
$command = $this->app->make(ModelsCommand::class); $command = $this->app->make(ModelsCommand::class);
@@ -19,7 +22,6 @@ class Test extends AbstractModelsCommand
$this->assertSame(0, $tester->getStatusCode()); $this->assertSame(0, $tester->getStatusCode());
$this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay()); $this->assertStringContainsString('Written new phpDocBlock to', $tester->getDisplay());
$this->assertMatchesMockedSnapshot(); $this->assertMatchesMockedSnapshot();
} }
} }
@@ -0,0 +1,22 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\PhpAttributesClosureInAttribute\Models;
use Illuminate\Database\Eloquent\Model;
// Tests: closure expression inside an attribute argument (PHP 8.5+)
/**
* @property int $id
* @method static \Illuminate\Database\Eloquent\Builder<static>|ClosureInAttribute newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ClosureInAttribute newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ClosureInAttribute query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ClosureInAttribute whereId($value)
* @mixin \Eloquent
*/
#[SomeClosureAttr(fn() => true)]
class ClosureInAttribute extends Model
{
protected $table = 'simples';
}
@@ -17,21 +17,6 @@ class ModelWithRelations extends Model
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id'); return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
} }
public function softDeletableWithTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withTrashed();
}
public function softDeletableOnlyTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->onlyTrashed();
}
public function softDeletableWithoutTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withoutTrashed();
}
public function nonSoftDeletable(): BelongsTo public function nonSoftDeletable(): BelongsTo
{ {
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id'); return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
@@ -16,9 +16,6 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletable * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletable
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletableOnlyTrashed
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletableWithTrashed
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletableWithoutTrashed
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query()
@@ -36,21 +33,6 @@ class ModelWithRelations extends Model
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id'); return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
} }
public function softDeletableWithTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withTrashed();
}
public function softDeletableOnlyTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->onlyTrashed();
}
public function softDeletableWithoutTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withoutTrashed();
}
public function nonSoftDeletable(): BelongsTo public function nonSoftDeletable(): BelongsTo
{ {
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id'); return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
@@ -16,9 +16,6 @@ use Illuminate\Database\Eloquent\Relations\HasOne;
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\NonSoftDeletableModel|null $nonSoftDeletableHasOne
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletable * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletable
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne * @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableHasOne
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableOnlyTrashed
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel $softDeletableWithTrashed
* @property-read \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\SoftDeletesRelations\Models\SoftDeletableModel|null $softDeletableWithoutTrashed
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query() * @method static \Illuminate\Database\Eloquent\Builder<static>|ModelWithRelations query()
@@ -36,21 +33,6 @@ class ModelWithRelations extends Model
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id'); return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id');
} }
public function softDeletableWithTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withTrashed();
}
public function softDeletableOnlyTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->onlyTrashed();
}
public function softDeletableWithoutTrashed(): BelongsTo
{
return $this->belongsTo(SoftDeletableModel::class, 'soft_deletable_model_id')->withoutTrashed();
}
public function nonSoftDeletable(): BelongsTo public function nonSoftDeletable(): BelongsTo
{ {
return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id'); return $this->belongsTo(NonSoftDeletableModel::class, 'non_soft_deletable_model_id');
@@ -1,12 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UniqueColumn\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected $table = 'users_unique';
}
@@ -1,30 +0,0 @@
<?php
declare(strict_types=1);
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\UniqueColumn\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $id
* @property string $name
* @property string $email
* @property string $password
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User newQuery()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User query()
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePassword($value)
* @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
* @mixin \Eloquent
*/
class User extends Model
{
protected $table = 'users_unique';
}
@@ -1,21 +0,0 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class UsersUniqueTable extends Migration
{
public function up(): void
{
Schema::create('users_unique', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('email')->unique();
$table->string('password');
$table->timestamps();
});
}
}
+3
View File
@@ -101,6 +101,7 @@ DOC;
* @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column * @param (\Closure(static): mixed)|string|array|\Illuminate\Contracts\Database\Query\Expression $column
* @param mixed $operator * @param mixed $operator
* @param mixed $value * @param mixed $value
* @param string $boolean
* @return \Illuminate\Database\Eloquent\Builder<static> * @return \Illuminate\Database\Eloquent\Builder<static>
* @static * @static
*/ */
@@ -129,6 +130,8 @@ DOC;
* Add a "where null" clause to the query. * Add a "where null" clause to the query.
* *
* @param string|array|\Illuminate\Contracts\Database\Query\Expression $columns * @param string|array|\Illuminate\Contracts\Database\Query\Expression $columns
* @param string $boolean
* @param bool $not
* @return \Illuminate\Database\Eloquent\Builder<static> * @return \Illuminate\Database\Eloquent\Builder<static>
* @static * @static
*/ */