-
Notifications
You must be signed in to change notification settings - Fork 89
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #104 from bfiessinger/fix/getMeta-empty-if-propert…
…y-is-casted [2.2.1] Fix getMeta returns empty Collection if attribute is casted in some cases
- Loading branch information
Showing
9 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
namespace Kodeine\Metable\Tests\Casts; | ||
|
||
use Exception; | ||
use Illuminate\Contracts\Database\Eloquent\CastsAttributes; | ||
|
||
class StateCaster implements CastsAttributes | ||
{ | ||
private $baseStateClass; | ||
|
||
public function __construct(string $baseStateClass) { | ||
$this->baseStateClass = $baseStateClass; | ||
} | ||
|
||
public function get($model, $key, $value, $attributes) { | ||
if ( is_null( $value ) ) return null; | ||
|
||
if ( ! is_subclass_of( $value, $this->baseStateClass ) ) { | ||
return null; | ||
} | ||
|
||
$stateClassName = $value::config()['default']; | ||
|
||
return new $stateClassName( $model ); | ||
} | ||
|
||
/** | ||
* @throws Exception | ||
*/ | ||
public function set($model, $key, $value, $attributes) { | ||
if ( is_null( $value ) ) return null; | ||
|
||
if ( ! is_subclass_of( $value, $this->baseStateClass ) ) { | ||
throw new Exception( 'Invalid state class.' ); | ||
} | ||
|
||
$value = new $value( $model ); | ||
|
||
if ( $value instanceof $this->baseStateClass ) { | ||
$value->setField( $key ); | ||
} | ||
|
||
return $value->getMorphClass(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
<?php | ||
|
||
namespace Kodeine\Metable\Tests\Casts; | ||
|
||
use Illuminate\Contracts\Database\Eloquent\Castable; | ||
|
||
abstract class UserCastedObject implements Castable | ||
{ | ||
public static $name; | ||
public $model; | ||
public $stateConfig; | ||
public $field; | ||
|
||
public function __construct($model) { | ||
$this->model = $model; | ||
$this->stateConfig = static::config(); | ||
} | ||
|
||
public static function config() { | ||
return [ | ||
'default' => null, | ||
]; | ||
} | ||
|
||
public static function castUsing(array $arguments) { | ||
return new StateCaster( static::class ); | ||
} | ||
|
||
public function setField(string $field): self { | ||
$this->field = $field; | ||
|
||
return $this; | ||
} | ||
|
||
public static function getMorphClass(): string { | ||
return static::$name ?? static::class; | ||
} | ||
|
||
public function make(?string $name, $model) { | ||
if ( is_null( $name ) ) { | ||
return null; | ||
} | ||
|
||
return new $name( $model ); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace Kodeine\Metable\Tests\Casts\UserState; | ||
|
||
class DefaultState extends State | ||
{ | ||
public $description; | ||
|
||
/** @noinspection PhpMissingParentConstructorInspection */ | ||
public function __construct() { | ||
$this->description = $this->description(); | ||
} | ||
|
||
public function description(): string { | ||
return 'This is a default description.'; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace Kodeine\Metable\Tests\Casts\UserState; | ||
|
||
use Kodeine\Metable\Tests\Casts\UserCastedObject; | ||
|
||
abstract class State extends UserCastedObject | ||
{ | ||
abstract public function description(): string; | ||
|
||
public static function config() | ||
{ | ||
return [ | ||
'default' => DefaultState::class, | ||
]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
use Kodeine\Metable\Tests\Models\UserTest; | ||
use Illuminate\Database\Capsule\Manager as Capsule; | ||
use Illuminate\Database\Eloquent\ModelNotFoundException; | ||
use Kodeine\Metable\Tests\Casts\UserState\DefaultState; | ||
|
||
class MetableTest extends TestCase | ||
{ | ||
|
@@ -31,6 +32,8 @@ public static function setUpBeforeClass(): void { | |
$table->string( 'name' )->default( 'john' ); | ||
$table->string( 'email' )->default( '[email protected]' ); | ||
$table->string( 'password' )->nullable(); | ||
$table->string( 'state' )->nullable(); | ||
$table->string( 'null_value' )->nullable(); | ||
$table->integer( 'user_test_id' )->unsigned()->nullable(); | ||
$table->foreign( 'user_test_id' )->references( 'id' )->on( 'user_tests' ); | ||
$table->timestamps(); | ||
|
@@ -47,6 +50,16 @@ public static function setUpBeforeClass(): void { | |
} ); | ||
} | ||
|
||
public function testCast() { | ||
$user = new UserTest; | ||
|
||
$this->assertNull( $user->state, 'Casted object should be null by default' ); | ||
|
||
$user->state = DefaultState::class; | ||
|
||
$this->assertInstanceOf( DefaultState::class, $user->state, 'Casted object should be instanceof DefaultState' ); | ||
} | ||
|
||
public function testFluentMeta() { | ||
$user = new UserTest; | ||
|
||
|
@@ -112,6 +125,21 @@ public function testFluentMeta() { | |
$this->assertTrue( $user->isMetaDirty( 'foo', 'bar' ), 'isMetaDirty should return true even if one of metas has changed' ); | ||
$this->assertTrue( $user->isMetaDirty( 'foo,bar' ), 'isMetaDirty should return true even if one of metas has changed' ); | ||
|
||
//re retrieve user from database | ||
/** @var UserTest $user */ | ||
$user = UserTest::find( $user->id ); | ||
|
||
$this->assertNull( $user->null_value, 'null_value property should be null' ); | ||
$this->assertNull( $user->null_cast, 'null_cast property should be null' ); | ||
|
||
$user->setMeta( 'null_value', true ); | ||
$user->setMeta( 'null_cast', true ); | ||
|
||
$this->assertTrue( $user->getMeta( 'null_value' ), 'Meta should be set' ); | ||
$this->assertTrue( $user->getMeta( 'null_cast' ), 'Meta should be set' ); | ||
$this->assertNull( $user->null_value, 'null_value property should be null' ); | ||
$this->assertNull( $user->null_cast, 'null_cast property should be null' ); | ||
|
||
$user->delete(); | ||
|
||
$this->assertEquals( 0, $metaData->count(), 'Meta should be deleted from database after deleting user.' ); | ||
|
@@ -227,6 +255,10 @@ public function testMetaMethods() { | |
|
||
$user->save(); | ||
|
||
$meta = $user->getMeta(); | ||
$this->assertInstanceOf( 'Illuminate\Support\Collection', $meta, 'Meta method getMeta is not typeof Collection' ); | ||
$this->assertTrue( $meta->isNotEmpty(), 'Meta method getMeta did return empty collection' ); | ||
|
||
// re retrieve user to make sure meta is saved | ||
$user = UserTest::with( ['metas'] )->find( $user->getKey() ); | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
<?php | ||
|
||
namespace Kodeine\Metable\Tests\Traits; | ||
|
||
use Kodeine\Metable\Tests\Casts\UserState\State; | ||
use Kodeine\Metable\Tests\Casts\UserCastedObject; | ||
|
||
trait HasUserStates | ||
{ | ||
public $casted = [ | ||
'state' => State::class, | ||
]; | ||
|
||
public static function bootHasUserCasts() { | ||
self::creating( function ($model) { | ||
$model->setDefaultCastedProperties(); | ||
} ); | ||
} | ||
|
||
public function initializeHasUserCasts() { | ||
$this->setDefaultCastedProperties(); | ||
} | ||
|
||
private function getStateConfigs() { | ||
$casts = $this->getCasts(); | ||
|
||
$states = []; | ||
|
||
foreach ($casts as $prop => $state) { | ||
if ( ! is_subclass_of( $state, UserCastedObject::class ) ) { | ||
continue; | ||
} | ||
|
||
$states[$prop] = $state::config(); | ||
} | ||
|
||
return $states; | ||
} | ||
|
||
private function setDefaultCastedProperties() { | ||
foreach ($this->getStateConfigs() as $prop => $config) { | ||
if ( $this->{$prop} === null ) { | ||
continue; | ||
} | ||
|
||
if ( ! isset( $config['default'] ) ) { | ||
continue; | ||
} | ||
|
||
$this->{$prop} = $config['default']; | ||
} | ||
} | ||
} |