Skip to content

Commit

Permalink
Merge pull request #42 from clementbirkle/main
Browse files Browse the repository at this point in the history
Retrieve phpType Attribute from Cast, Fallback to Column Type
  • Loading branch information
freekmurze authored Jul 16, 2024
2 parents 91bfe67 + 06361db commit eeeeaaf
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .phpunit.cache/test-results
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"version":"pest_2.34.8","defects":[],"times":{"P\\Tests\\RelationTest::__pest_evaluable_it_will_find_no_relations_on_a_model_that_has_none":0,"P\\Tests\\RelationTest::__pest_evaluable_it_can_get_the_model_info_of_the_related_model":0.001,"P\\Tests\\RelationTest::__pest_evaluable_it_can_find_the_relations_on_a_model":0.001,"P\\Tests\\ModelFinderTest::__pest_evaluable_it_can_discover_all_models_in_a_directory":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_get_extended_column_types_for_a_model":0.002,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_get_the_attributes_of_a_model":0.002,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_get_the_accessor_attributes_of_a_model":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_get_the_mutator_attributes_of_a_model":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_handle_virtual_attributes_of_a_model":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_handle_accessor_mutator_combinations":0.002,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_meta_information_about_all_models":0.037,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_traits_from_a_model":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_it_will_return_null_when_getting_a_non_existing_attribute":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_a_specific_relation":0.002,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_extra_info_from_a_model":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_a_specific_attribute":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_it_will_return_null_when_getting_a_non_existing_relation":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_meta_information_about_a_model":0.013}}
{"version":"pest_2.34.8","defects":[],"times":{"P\\Tests\\RelationTest::__pest_evaluable_it_will_find_no_relations_on_a_model_that_has_none":0.001,"P\\Tests\\RelationTest::__pest_evaluable_it_can_get_the_model_info_of_the_related_model":0.001,"P\\Tests\\RelationTest::__pest_evaluable_it_can_find_the_relations_on_a_model":0.001,"P\\Tests\\ModelFinderTest::__pest_evaluable_it_can_discover_all_models_in_a_directory":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_get_extended_column_types_for_a_model":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_get_the_attributes_of_a_model":0.002,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_get_the_accessor_attributes_of_a_model":0.002,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_get_the_mutator_attributes_of_a_model":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_handle_virtual_attributes_of_a_model":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_can_handle_accessor_mutator_combinations":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_meta_information_about_all_models":0.006,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_traits_from_a_model":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_it_will_return_null_when_getting_a_non_existing_attribute":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_a_specific_relation":0.002,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_extra_info_from_a_model":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_a_specific_attribute":0.033,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_it_will_return_null_when_getting_a_non_existing_relation":0.001,"P\\Tests\\ModelInfoTest::__pest_evaluable_it_can_get_meta_information_about_a_model":0.013,"P\\Tests\\AttributeTest::__pest_evaluable_it_phpType_attribute_from_cast_and_after_from_database":0.001,"P\\Tests\\AttributeTest::__pest_evaluable_it_retrieves_phpType_attribute_from_cast_and_falls_back_to_column_type":0.001}}
52 changes: 48 additions & 4 deletions src/Attributes/AttributeFinder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Spatie\ModelInfo\Attributes;

use BackedEnum;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Casts\AsStringable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
Expand Down Expand Up @@ -40,10 +42,11 @@ protected function attributes(Model $model): Collection
->values()
->map(function (array $column) use ($model, $indexes) {
$columnIndexes = $this->getIndexes($column['name'], $indexes);
$cast = $this->getCastType($column['name'], $model);

return new Attribute(
name: $column['name'],
phpType: $this->getPhpType($column),
phpType: $this->getPhpType($cast, $column),
type: $column['type'],
increments: $column['auto_increment'],
nullable: $column['nullable'],
Expand All @@ -52,15 +55,56 @@ protected function attributes(Model $model): Collection
unique: $columnIndexes->contains(fn (array $index) => $index['unique']),
fillable: $model->isFillable($column['name']),
appended: null,
cast: $this->getCastType($column['name'], $model),
cast: $cast,
virtual: false,
hidden: $this->attributeIsHidden($column['name'], $model)
);
})
->merge($this->getVirtualAttributes($model, $columns));
}

protected function getPhpType(array $column): string
protected function getPhpType(?string $cast, array $column): string
{
$type = $this->getPhpTypeFromCast($cast);

$type ??= $this->getPhpTypeFromColumn($column);

return $type;
}

protected function getPhpTypeFromCast(?string $cast): ?string
{
if (! $cast) {
return null;
}

$castFirstPart = explode(':', $cast)[0];

$type = match ($castFirstPart) {
'array' => 'array',
'boolean' => 'bool',
'float', 'decimal', 'double', 'real' => 'float',
'integer' => 'int',
'object' => 'object',
'AsStringable' => '\\'.AsStringable::class,
'collection', 'AsEnumCollection' => '\\'.Collection::class,
'date', 'datetime', 'timestamp', 'immutable_date', 'immutable_datetime' => '\\'.CarbonInterface::class,
default => null,
};

$type ??= match ($cast) {
'encrypted:array' => 'array',
'encrypted:collection', 'AsEncryptedCollection' => '\\'.Collection::class,
'encrypted:object', 'AsEncryptedArrayObject' => 'object',
default => null,
};

$type ??= enum_exists($cast) ? '\\'.$cast : null;

return $type;
}

protected function getPhpTypeFromColumn(array $column): string
{
$type = match ($column['type']) {
'tinyint(1)', 'bit' => 'bool',
Expand All @@ -72,7 +116,7 @@ protected function getPhpType(array $column): string
'float', 'real', 'float4', 'double', 'float8' => 'float',
'binary', 'varbinary', 'bytea', 'image', 'blob', 'tinyblob', 'mediumblob', 'longblob' => 'resource',
'boolean', 'bool' => 'bool',
'date', 'time', 'timetz', 'datetime', 'datetime2', 'smalldatetime', 'datetimeoffset', 'timestamp', 'timestamptz' => 'DateTime',
'date', 'time', 'timetz', 'datetime', 'datetime2', 'smalldatetime', 'datetimeoffset', 'timestamp', 'timestamptz' => '\\'.CarbonInterface::class,
'json', 'jsonb' => 'mixed',
// 'char', 'bpchar', 'nchar',
// 'varchar', 'nvarchar',
Expand Down
13 changes: 13 additions & 0 deletions tests/AttributeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

use Illuminate\Support\Collection;
use Spatie\ModelInfo\Attributes\AttributeFinder;
use Spatie\ModelInfo\Tests\TestSupport\Enums\TestEnum;
use Spatie\ModelInfo\Tests\TestSupport\Models\ExtendedTypesModel;
use Spatie\ModelInfo\Tests\TestSupport\Models\PhpTypeFromCastModel;
use Spatie\ModelInfo\Tests\TestSupport\Models\TestModel;

use function Spatie\Snapshots\assertMatchesSnapshot;
Expand Down Expand Up @@ -88,6 +90,17 @@
matchesAttributesSnapshot($attributes);
});

it('retrieves phpType attribute from cast and falls back to column type', function () {
$attributes = AttributeFinder::forModel(new PhpTypeFromCastModel());

expect($attributes->pluck('phpType')->toArray())->toBe([
'array',
'\\'.Collection::class,
'\\'.TestEnum::class,
'int',
]);
});

function matchesAttributesSnapshot(Collection $attributes)
{
$attributes = $attributes->map->toArray();
Expand Down
4 changes: 3 additions & 1 deletion tests/ModelFinderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Spatie\ModelInfo\Tests\TestSupport\Models\ExtendedTypesModel;
use Spatie\ModelInfo\Tests\TestSupport\Models\ExtraModelInfoModel;
use Spatie\ModelInfo\Tests\TestSupport\Models\Nested\Model\NestedModel;
use Spatie\ModelInfo\Tests\TestSupport\Models\PhpTypeFromCastModel;
use Spatie\ModelInfo\Tests\TestSupport\Models\RelationTestModel;
use Spatie\ModelInfo\Tests\TestSupport\Models\TestModel;
use Spatie\ModelInfo\Tests\TestSupport\Models\TraitTestModel;
Expand All @@ -17,11 +18,12 @@
"Spatie\ModelInfo\Tests",
);

expect($models)->toHaveCount(6);
expect($models)->toHaveCount(7);

expect($models->toArray())->toEqualCanonicalizing([
NestedModel::class,
ExtraModelInfoModel::class,
PhpTypeFromCastModel::class,
RelationTestModel::class,
TestModel::class,
TraitTestModel::class,
Expand Down
2 changes: 1 addition & 1 deletion tests/ModelInfoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"Spatie\ModelInfo\Tests",
);

expect($modelInfo)->toHaveCount(6);
expect($modelInfo)->toHaveCount(7);
expect($modelInfo->first())->toBeInstanceOf(ModelInfo::class);
});

Expand Down
5 changes: 5 additions & 0 deletions tests/TestSupport/Enums/TestEnum.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

namespace Spatie\ModelInfo\Tests\TestSupport\Enums;

enum TestEnum {}
23 changes: 23 additions & 0 deletions tests/TestSupport/Models/PhpTypeFromCastModel.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Spatie\ModelInfo\Tests\TestSupport\Models;

use Illuminate\Database\Eloquent\Model;
use Spatie\ModelInfo\Tests\TestSupport\Enums\TestEnum;

class PhpTypeFromCastModel extends Model
{
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'array' => 'array',
'encryptedCollection' => 'encrypted:collection',
'enum' => TestEnum::class,
];
}
}
7 changes: 7 additions & 0 deletions tests/TestSupport/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ public function getEnvironmentSetUp($app)
}
$table->time('time');
});

Schema::create('php_type_from_cast_models', function (Blueprint $table) {
$table->longText('array');
$table->longText('encryptedCollection');
$table->string('enum');
$table->integer('integer');
});
}

public function getTestSupportDirectory(string $suffix = ''): string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
hidden: false
-
name: time
phpType: DateTime
phpType: \Carbon\CarbonInterface
type: time
increments: false
nullable: false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
hidden: false
-
name: created_at
phpType: DateTime
phpType: \Carbon\CarbonInterface
type: datetime
increments: false
nullable: true
Expand All @@ -70,7 +70,7 @@
hidden: false
-
name: updated_at
phpType: DateTime
phpType: \Carbon\CarbonInterface
type: datetime
increments: false
nullable: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ relations:
attributes:
- { name: id, phpType: int, type: integer, increments: true, nullable: false, default: null, primary: true, unique: true, fillable: false, appended: null, cast: int, virtual: false, hidden: false }
- { name: name, phpType: string, type: varchar, increments: false, nullable: false, default: null, primary: false, unique: false, fillable: false, appended: null, cast: null, virtual: false, hidden: false }
- { name: created_at, phpType: DateTime, type: datetime, increments: false, nullable: true, default: null, primary: false, unique: false, fillable: false, appended: null, cast: datetime, virtual: false, hidden: false }
- { name: updated_at, phpType: DateTime, type: datetime, increments: false, nullable: true, default: null, primary: false, unique: false, fillable: false, appended: null, cast: datetime, virtual: false, hidden: false }
- { name: created_at, phpType: \Carbon\CarbonInterface, type: datetime, increments: false, nullable: true, default: null, primary: false, unique: false, fillable: false, appended: null, cast: datetime, virtual: false, hidden: false }
- { name: updated_at, phpType: \Carbon\CarbonInterface, type: datetime, increments: false, nullable: true, default: null, primary: false, unique: false, fillable: false, appended: null, cast: datetime, virtual: false, hidden: false }
traits: { }
extra: null

0 comments on commit eeeeaaf

Please sign in to comment.