Skip to content

Commit

Permalink
add json support
Browse files Browse the repository at this point in the history
  • Loading branch information
lsmith77 committed Jul 25, 2021
1 parent 043b311 commit 599fa0c
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 14 deletions.
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,93 @@ All timestamps and the optional soft-delete timestamp will be ignored.

<a name="exclude" />

### Using JSON encoding

Run

```
php artisan vendor:publish --provider="Mpociot\Versionable\Providers\ServiceProvider" --tag="config"
```

Adjust the encoding in the config from `serialize` to `json`.

Create and run a migration like the following:

```
<?php
use Illuminate\Database\Migrations\Migration;
use Mpociot\Versionable\Version;
class Encoding extends Migration
{
protected $encodingCheck = [
'serialize' => '{',
'json' => 'a:',
];
protected $chunkSize = 10;
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
$targetEncoding = config('versionable.encoding');
$sourceEncoding = $targetEncoding === 'json' ? 'serialize' : 'json';
$this->changeEncoding($targetEncoding, $sourceEncoding);
Schema::table('versions', function ($table) {
$table->json('model_data')->change();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
$sourceEncoding = config('versionable.encoding');
$targetEncoding = $sourceEncoding === 'json' ? 'serialize' : 'json';
$this->changeEncoding($targetEncoding, $sourceEncoding);
Schema::table('versions', function ($table) {
$table->longText('model_data')->change();
});
}
protected function changeEncoding($targetEncoding, $sourceEncoding)
{
$versions = Version::lazy($this->chunkSize);
foreach ($versions as $version) {
$this->validateData($version, $sourceEncoding);
$version->model_data = $targetEncoding === 'serialize'
? serialize(json_decode($version->model_data, true))
: json_encode(unserialize($version->model_data));
$version->save();
}
return true;
}
protected function validateData(Version $version, $sourceEncoding)
{
if (strpos($version->model_data, $this->encodingCheck[$sourceEncoding]) === 0) {
throw new RuntimeException("Wrong source encoding while trying to convert from '$sourceEncoding': ".substr($version->model_data, 0, 10).'..');
}
}
}
```

### Exclude attributes from versioning

Sometimes you don't want to create a version *every* time an attribute on your model changes. For example your User model might have a `last_login_at` attribute.
Expand Down
12 changes: 11 additions & 1 deletion src/Mpociot/Versionable/Version.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ public function versionable()
return $this->morphTo();
}

/**
* Return the encoding
* @return mixed
*/
private function getEncoding()
{
return config('versionable.encoding', 'serialize');
}

/**
* Return the user responsible for this version
* @return mixed
Expand All @@ -50,10 +59,11 @@ public function getModel()
$modelData = is_resource($this->model_data)
? stream_get_contents($this->model_data,-1,0)
: $this->model_data;
$modelDataEncoded = $this->getEncoding() === 'json' ? json_decode($modelData, true) : unserialize($modelData);

$model = new $this->versionable_type();
$model->unguard();
$model->fill(unserialize($modelData));
$model->fill($modelDataEncoded);
$model->exists = true;
$model->reguard();
return $model;
Expand Down
24 changes: 18 additions & 6 deletions src/Mpociot/Versionable/VersionableTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ trait VersionableTrait
/**
* Retrieve, if exists, the property that define that Version model.
* If no property defined, use the default Version model.
*
*
* Trait cannot share properties whth their class !
* http://php.net/manual/en/language.oop5.traits.php
* @return unknown|string
Expand All @@ -28,6 +28,16 @@ protected function getVersionClass()
return config('versionable.version_model', Version::class);
}

/**
* Get the encoding, the default is serialize.
*
* @return string
*/
protected function getEncoding()
{
return config('versionable.encoding', 'serialize');
}

/**
* Private variable to detect if this is an update
* or an insert
Expand Down Expand Up @@ -173,10 +183,12 @@ protected function versionablePostSave()
$version->versionable_id = $this->getKey();
$version->versionable_type = method_exists($this, 'getMorphClass') ? $this->getMorphClass() : get_class($this);
$version->user_id = $this->getAuthUserId();

$versionedHiddenFields = $this->versionedHiddenFields ?? [];
$this->makeVisible($versionedHiddenFields);
$version->model_data = serialize($this->attributesToArray());
$version->model_data = $this->getEncoding() === 'json'
? json_encode($this->attributesToArray())
: serialize($this->attributesToArray());
$this->makeHidden($versionedHiddenFields);

if (!empty( $this->reason )) {
Expand All @@ -191,16 +203,16 @@ protected function versionablePostSave()

/**
* Delete old versions of this model when the reach a specific count.
*
*
* @return void
*/
private function purgeOldVersions()
{
$keep = isset($this->keepOldVersions) ? $this->keepOldVersions : 0;

if ((int)$keep > 0) {
$count = $this->versions()->count();

if ($count > $keep) {
$this->getLatestVersions()
->take($count)
Expand Down
9 changes: 7 additions & 2 deletions src/config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
* Feel free to change this, if you need specific version
* model logic.
*/
'version_model' => \Mpociot\Versionable\Version::class
'version_model' => \Mpociot\Versionable\Version::class,

];
/*
* The encoding to use for the model data encoding.
* Default is 'serialize' and uses PHP serialize() but 'json' is also supported
*/
'encoding' => 'serialize',
];
27 changes: 22 additions & 5 deletions tests/VersionableTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -460,19 +460,19 @@ public function testKeepMaxVersionCount()
$name_v2 = 'second' ;
$name_v3 = 'third' ;
$name_v4 = 'fourth' ;

$model = new ModelWithMaxVersions();
$model->email = "[email protected]";
$model->password = "foo";
$model->name = $name_v1 ;
$model->save();

$model->name = $name_v2 ;
$model->save();

$model->name = $name_v3 ;
$model->save();

$model->name = $name_v4 ;
$model->save();

Expand Down Expand Up @@ -513,7 +513,24 @@ public function testAllowHiddenFields() {

$this->assertArrayNotHasKey('password', $user->toArray());
}


public function testVersionWithJson()
{
$this->app['config']->set('versionable.encoding', 'json');

$user = new TestVersionableUser();
$user->name = "Marcel";
$user->email = "[email protected]";
$user->password = "12345";
$user->last_login = $user->freshTimestamp();
$user->save();

$version = $user->currentVersion();

$this->assertStringStartsWith( '{', $version->model_data, 'Model data is not json encoded' );
$this->assertEquals( $user->attributesToArray(), $version->getModel()->attributesToArray() );
}

}


Expand Down

0 comments on commit 599fa0c

Please sign in to comment.