Skip to content

Commit 744a9cf

Browse files
authored
support Castables (#1287)
1 parent f2a7f9d commit 744a9cf

File tree

6 files changed

+95
-2
lines changed

6 files changed

+95
-2
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
99

1010
### Added
1111
- 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)
12+
- Add support for custom casts that using `Castable` [#1287 / binotaliu](https://github.com/barryvdh/laravel-ide-helper/pull/1287)
1213

1314
### Fixed
1415
- Fix recursively searching for `HasFactory` and `Macroable` traits [\#1216 / daniel-de-wit](https://github.com/barryvdh/laravel-ide-helper/pull/1216)

src/Console/ModelsCommand.php

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Doctrine\DBAL\Exception as DBALException;
2121
use Doctrine\DBAL\Types\Type;
2222
use Illuminate\Console\Command;
23+
use Illuminate\Contracts\Database\Eloquent\Castable;
2324
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
2425
use Illuminate\Database\Eloquent\Casts\Attribute;
2526
use Illuminate\Database\Eloquent\Factories\Factory;
@@ -366,6 +367,9 @@ public function castPropertiesType($model)
366367
} elseif (Str::startsWith($type, 'encrypted:')) {
367368
$type = Str::after($type, ':');
368369
}
370+
371+
$params = [];
372+
369373
switch ($type) {
370374
case 'encrypted':
371375
$realType = 'mixed';
@@ -412,13 +416,17 @@ public function castPropertiesType($model)
412416
$type = strtok($type, ':');
413417
$realType = class_exists($type) ? ('\\' . $type) : 'mixed';
414418
$this->setProperty($name, null, true, true);
419+
420+
$params = strtok(':');
421+
$params = $params ? explode(',', $params) : [];
415422
break;
416423
}
417424

418425
if (!isset($this->properties[$name])) {
419426
continue;
420427
}
421428

429+
$realType = $this->checkForCastableCasts($realType, $params);
422430
$realType = $this->checkForCustomLaravelCasts($realType);
423431
$realType = $this->getTypeOverride($realType);
424432
$this->properties[$name]['type'] = $this->getTypeInModel($model, $realType);
@@ -1141,9 +1149,9 @@ protected function getCommentFromDocBlock(\ReflectionMethod $reflection)
11411149
*
11421150
* @return null|string
11431151
*/
1144-
protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection)
1152+
protected function getReturnTypeFromDocBlock(\ReflectionMethod $reflection, \Reflector $reflectorForContext = null)
11451153
{
1146-
$phpDocContext = (new ContextFactory())->createFromReflector($reflection);
1154+
$phpDocContext = (new ContextFactory())->createFromReflector($reflectorForContext ?? $reflection);
11471155
$context = new Context(
11481156
$phpDocContext->getNamespace(),
11491157
$phpDocContext->getNamespaceAliases()
@@ -1260,6 +1268,32 @@ protected function getClassKeyword(ReflectionClass $reflection)
12601268
return $keyword;
12611269
}
12621270

1271+
protected function checkForCastableCasts(string $type, array $params = []): string
1272+
{
1273+
if (!class_exists($type) || !interface_exists(Castable::class)) {
1274+
return $type;
1275+
}
1276+
1277+
$reflection = new \ReflectionClass($type);
1278+
1279+
if (!$reflection->implementsInterface(Castable::class)) {
1280+
return $type;
1281+
}
1282+
1283+
$cast = call_user_func([$type, 'castUsing'], $params);
1284+
1285+
if (is_string($cast) && !is_object($cast)) {
1286+
return $cast;
1287+
}
1288+
1289+
$castReflection = new ReflectionObject($cast);
1290+
1291+
$methodReflection = $castReflection->getMethod('get');
1292+
1293+
return $this->getReturnTypeFromReflection($methodReflection) ??
1294+
$this->getReturnTypeFromDocBlock($methodReflection, $reflection);
1295+
}
1296+
12631297
/**
12641298
* @param string $type
12651299
* @return string|null
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts;
6+
7+
use Illuminate\Contracts\Database\Eloquent\Castable;
8+
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
9+
10+
class CastableReturnsAnonymousCaster implements Castable
11+
{
12+
public static function castUsing(array $arguments)
13+
{
14+
return new class() implements CastsAttributes {
15+
/**
16+
* @inheritDoc
17+
* @return CastedProperty
18+
*/
19+
public function get($model, string $key, $value, array $attributes)
20+
{
21+
return new CastedProperty();
22+
}
23+
24+
/**
25+
* @inheritDoc
26+
*/
27+
public function set($model, string $key, $value, array $attributes)
28+
{
29+
// TODO: Implement set() method.
30+
}
31+
};
32+
}
33+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts;
6+
7+
use Illuminate\Contracts\Database\Eloquent\Castable;
8+
9+
class CastableReturnsCustomCaster implements Castable
10+
{
11+
public static function castUsing(array $arguments)
12+
{
13+
return CustomCasterWithDocblockReturn::class;
14+
}
15+
}

tests/Console/ModelsCommand/LaravelCustomCasts/Models/CustomCast.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Models;
66

7+
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableReturnsAnonymousCaster;
8+
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableReturnsCustomCaster;
79
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithDocblockReturn;
810
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithDocblockReturnFqn;
911
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithNullablePrimitiveReturn;
@@ -31,6 +33,8 @@ class CustomCast extends Model
3133
'casted_property_with_param' => CustomCasterWithParam::class . ':param',
3234
'casted_property_with_static_return_docblock' => SelfCastingCasterWithStaticDocblockReturn::class,
3335
'casted_property_with_this_return_docblock' => SelfCastingCasterWithThisDocblockReturn::class,
36+
'casted_property_with_castable' => CastableReturnsCustomCaster::class,
37+
'casted_property_with_anonymous_cast' => CastableReturnsAnonymousCaster::class,
3438
'extended_casted_property_with_static_return_docblock' => ExtendedSelfCastingCasterWithStaticDocblockReturn::class,
3539
'extended_casted_property_with_this_return_docblock' => ExtendedSelfCastingCasterWithThisDocblockReturn::class,
3640
'casted_property_with_static_return_docblock_and_param' => SelfCastingCasterWithStaticDocblockReturn::class . ':param',

tests/Console/ModelsCommand/LaravelCustomCasts/__snapshots__/Test__test__1.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Models;
66

7+
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableReturnsAnonymousCaster;
8+
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastableReturnsCustomCaster;
79
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithDocblockReturn;
810
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithDocblockReturnFqn;
911
use Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CustomCasterWithNullablePrimitiveReturn;
@@ -34,6 +36,8 @@
3436
* @property ExtendedSelfCastingCasterWithStaticDocblockReturn $extended_casted_property_with_static_return_docblock
3537
* @property ExtendedSelfCastingCasterWithThisDocblockReturn $extended_casted_property_with_this_return_docblock
3638
* @property SelfCastingCasterWithStaticDocblockReturn $casted_property_with_static_return_docblock_and_param
39+
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_castable
40+
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $casted_property_with_anonymous_cast
3741
* @property \Barryvdh\LaravelIdeHelper\Tests\Console\ModelsCommand\LaravelCustomCasts\Casts\CastedProperty $cast_without_property
3842
* @method static \Illuminate\Database\Eloquent\Builder|CustomCast newModelQuery()
3943
* @method static \Illuminate\Database\Eloquent\Builder|CustomCast newQuery()
@@ -66,6 +70,8 @@ class CustomCast extends Model
6670
'casted_property_with_param' => CustomCasterWithParam::class . ':param',
6771
'casted_property_with_static_return_docblock' => SelfCastingCasterWithStaticDocblockReturn::class,
6872
'casted_property_with_this_return_docblock' => SelfCastingCasterWithThisDocblockReturn::class,
73+
'casted_property_with_castable' => CastableReturnsCustomCaster::class,
74+
'casted_property_with_anonymous_cast' => CastableReturnsAnonymousCaster::class,
6975
'extended_casted_property_with_static_return_docblock' => ExtendedSelfCastingCasterWithStaticDocblockReturn::class,
7076
'extended_casted_property_with_this_return_docblock' => ExtendedSelfCastingCasterWithThisDocblockReturn::class,
7177
'casted_property_with_static_return_docblock_and_param' => SelfCastingCasterWithStaticDocblockReturn::class . ':param',

0 commit comments

Comments
 (0)