laravel-ide-helper icon indicating copy to clipboard operation
laravel-ide-helper copied to clipboard

Invalid phpdoc generated for custom cast without defined return type

Open eithed opened this issue 2 years ago • 0 comments

Versions:

  • ide-helper Version: 2.9.2, but exists on 2.10 as well
  • Laravel Version: 8.51.0
  • PHP Version: 8.0.8

Description:

When using a cast that implements CastsAttributes interface, if get method doesn't return a type, ie:

public function get($model, $key, $value, $attributes)

and given column is of json type then an invalid phpdoc is generated, like this:

* @property |null $premium

The reason for this is that in ModelsCommand class, in castPropertiesType method $realType = $this->checkForCustomLaravelCasts($realType); returns null, $realType = $this->getTypeOverride($realType); returns null, $this->properties[$name]['type'] = $this->getTypeInModel($model, $realType); returns null (which defacto overrides the type of property that has been set, in this case mixed|null). Then

if (isset($this->nullableColumns[$name])) {
    $this->properties[$name]['type'] .= '|null';
}

which evaluates to null.'|null' produces |null

Steps To Reproduce:

  1. Add field foo to users migration like this
class CreateUsersTable extends Migration
{
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->jsonb('foo')->nullable();
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }
}
  1. Make User use a custom cast:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use App\Casts\FooCast;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'email',
        'password',
        'foo'
    ];

    protected $casts = [
        'foo' => FooCast::class,
        'email_verified_at' => 'datetime',
    ];
}
  1. Create a cast without a defined type:
<?php

namespace App\Casts;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class FooCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes)
    {
        return json_encode($value, true);
    }

    /**
     * Prepare the given value for storage.
     */
    public function set($model, $key, $value, $attributes)
    {
        return json_decode($value, true);
    }
}

eithed avatar Jul 26 '21 14:07 eithed