diff --git a/.gitignore b/.gitignore
index e75f91b..e4a8caf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,5 @@
-composer.phar
/vendor/
-
-# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
-# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
+/logs/
+composer.phar
composer.lock
+clover.xml
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..fc0ef09
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,20 @@
+language: php
+php:
+ - "7.3"
+ - "7.2"
+ - "7.1"
+# env:
+# - PHPUNIT_VERSION=^7 TESTBENCH_VERSION=3.*
+# matrix:
+# include:
+# - php: 7.0
+# env: PHPUNIT_VERSION=^6 TESTBENCH_VERSION=3.5.*
+# before_install:
+# - composer require --dev "phpunit/phpunit:${PHPUNIT_VERSION}"
+# - composer require --dev "orchestra/testbench:${TESTBENCH_VERSION}"
+install:
+ - composer install -n
+script:
+ - composer test:dist
+after_success:
+ - travis_retry composer test:coverage
diff --git a/README.md b/README.md
index dcc0c02..23722ff 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,13 @@
+
+
+
+
+
+
+
+
# History
+
Eloquent model history tracking for Laravel
## Installation
@@ -121,7 +130,6 @@ $model->histories()->orderBy('performed_at', 'desc')->take(10)
$model->histories()->where('user_id', 10010)
```
-
### History
```php
@@ -201,3 +209,35 @@ This will translate your model history into
### Filters
You may set whitelist and blacklist in config file. Please follow the description guide in the published config file.
+
+### Known issues
+
+1. When updating a model, if its model label(attributes returned from `getModelLabel`) has been modified, the history message will use its new attributes, which might not be what you expect.
+
+```php
+class Article extends Model
+{
+ use HasHistories;
+
+ public function getModelLabel()
+ {
+ return $this->title;
+ }
+}
+// original title is 'my title'
+// modify title
+$article->title = 'new title';
+$article->save();
+// the updating history message
+// expect: Updating Article my title
+// actual: Updating Article new title
+```
+
+A workaround
+
+```php
+public function getModelLabel()
+{
+ return $this->getOriginal('title', $this->title);
+}
+```
diff --git a/composer.json b/composer.json
index 0764415..eaf6f51 100644
--- a/composer.json
+++ b/composer.json
@@ -5,7 +5,7 @@
"homepage": "https://github.com/seancheung/history",
"require": {
"php": ">=5.6.4",
- "illuminate/support": "^5.3"
+ "illuminate/support": "^5.3"
},
"license": "MIT",
"authors": [
@@ -18,5 +18,21 @@
"psr-4": {
"Panoscape\\History\\": "src/"
}
+ },
+ "scripts": {
+ "test": "phpunit",
+ "test:dist": "phpunit --coverage-clover clover.xml",
+ "test:coverage": "php-coveralls -v -x clover.xml -o ./logs --exclude-no-stmt"
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Panoscape\\History\\Tests\\": "tests/"
+ }
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~7.4",
+ "orchestra/testbench": "~3.7",
+ "mockery/mockery": "^1.2",
+ "php-coveralls/php-coveralls": "^2.1"
}
}
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..ae4f23d
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ tests/
+
+
+
+
+ src/
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/History.php b/src/History.php
index e9f4262..271ee9a 100644
--- a/src/History.php
+++ b/src/History.php
@@ -56,11 +56,11 @@ public function __construct(array $attributes = [])
}
/**
- * Get the user of this record
+ * Get the user who performed this record
*/
public function user()
{
- return $this->morphTo();
+ return $this->hasUser()? $this->morphTo()->first(): null;
}
/**
@@ -70,7 +70,7 @@ public function user()
*/
public function hasUser()
{
- return !empty($this->agent_type) && !empty($this->agent_id);
+ return !empty($this->user_type) && !empty($this->user_id);
}
/**
@@ -78,6 +78,6 @@ public function hasUser()
*/
public function model()
{
- return $this->morphTo();
+ return $this->morphTo()->first();
}
}
\ No newline at end of file
diff --git a/src/HistoryObserver.php b/src/HistoryObserver.php
index 7cfc703..c97e4fc 100644
--- a/src/HistoryObserver.php
+++ b/src/HistoryObserver.php
@@ -36,6 +36,11 @@ public function updating($model)
* Gets the model's altered values and tracks what had changed
*/
$changes = $model->getDirty();
+ /**
+ * Bypass restoring event
+ */
+ if(array_key_exists('deleted_at', $changes)) return;
+
$changed = [];
foreach ($changes as $key => $value) {
if(static::isIgnored($model, $key)) continue;
@@ -111,7 +116,7 @@ public static function isIgnored($model, $key)
{
$blacklist = config('history.attributes_blacklist');
$name = get_class($model);
- $array = $blacklist[$name];
+ $array = isset($blacklist[$name])? $blacklist[$name]: null;
return !empty($array) && in_array($key, $array);
}
diff --git a/tests/Article.php b/tests/Article.php
new file mode 100644
index 0000000..054ea21
--- /dev/null
+++ b/tests/Article.php
@@ -0,0 +1,20 @@
+getOriginal('title', $this->title);
+ }
+}
\ No newline at end of file
diff --git a/tests/TestCaseTest.php b/tests/TestCaseTest.php
new file mode 100644
index 0000000..472fa78
--- /dev/null
+++ b/tests/TestCaseTest.php
@@ -0,0 +1,181 @@
+ History::class
+ ];
+ }
+
+ protected function getEnvironmentSetUp($app)
+ {
+ $app['config']->set('app.debug', true);
+ $app['config']->set('database.default', 'testing');
+ $app['config']->set('history.console_enabled', true);
+ $app['config']->set('history.test_enabled', true);
+ $app['config']->set('history.attributes_blacklist', [
+ User::class => [
+ 'password'
+ ]
+ ]);
+
+ $app['router']->post('articles', function(Request $request) {
+ return Article::create(['title' => $request->title]);
+ });
+ $app['router']->put('articles/{id}', function(Request $request, $id) {
+ $model = Article::find($id);
+ $model->title = $request->title;
+ $model->save();
+ return $model;
+ });
+ $app['router']->delete('articles/{id}', function($id) {
+ Article::destroy($id);
+ });
+ $app['router']->post('articles/{id}/restore', function($id) {
+ Article::withTrashed()->find($id)->restore();
+ });
+ $app['router']->get('articles/{id}', function($id) {
+ $model = Article::find($id);
+ if(!is_null($model)) {
+ event(new ModelChanged($model, 'Query Article ' . $model->title, $model->pluck('id')->toArray()));
+ }
+ return $model;
+ });
+ }
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->setUpDatabase();
+ }
+
+ protected function setUpDatabase()
+ {
+ $builder = $this->app['db']->connection()->getSchemaBuilder();
+
+ $builder->create('users', function (Blueprint $table) {
+ $table->increments('id');
+ $table->string('name');
+ $table->string('password');
+ });
+
+ $builder->create('articles', function (Blueprint $table) {
+ $table->increments('id');
+ $table->string('title');
+ $table->softDeletes();
+ });
+
+ User::create(['name' => 'Esther', 'password' => '6ecd6a17b723']);
+
+ $this->loadMigrationsFrom(realpath(__DIR__.'/../src/migrations'));
+ }
+
+ public function testHistory()
+ {
+ $content = ['title' => 'enim officiis omnis'];
+ $this->json('POST', '/articles', $content)->assertJson($content);
+ $history = History::first();
+ $article = Article::first();
+ $this->assertNotNull($history);
+ $this->assertEquals(Article::class, $history->model_type);
+ $this->assertEquals($article->id, $history->model_id);
+ $this->assertEquals('Created Article ' . $content['title'], $history->message);
+ $this->assertTrue($history->performed_at instanceof \Illuminate\Support\Carbon);
+ $history->delete();
+
+ $data = ['title' => 'eligendi fugiat culpa'];
+ $this->json('PUT', '/articles/' . $article->id, $data)->assertJson($data);
+ $history = History::first();
+ $this->assertNotNull($history);
+ $this->assertEquals($article->id, $history->model_id);
+ $this->assertEquals('Updating Article ' . $content['title'], $history->message);
+ $this->assertEquals([['key' => 'title', 'old' => 'enim officiis omnis', 'new' => 'eligendi fugiat culpa']], $history->meta);
+ $history->delete();
+ $article->refresh();
+
+ $this->json('DELETE', '/articles/' . $article->id);
+ $history = History::first();
+ $this->assertNotNull($history);
+ $this->assertEquals($article->id, $history->model_id);
+ $this->assertEquals('Deleting Article ' . $article->title, $history->message);
+ $history->delete();
+
+ $this->json('POST', '/articles/' . $article->id . '/restore');
+ $history = History::first();
+ $this->assertNotNull($history);
+ $this->assertEquals($article->id, $history->model_id);
+ $this->assertEquals('Restored Article ' . $article->title, $history->message);
+ }
+
+ public function testAuthed()
+ {
+ $user = User::first();
+ $this->assertNotNull($user);
+
+ $content = ['title' => 'voluptas ut rem'];
+ $this->actingAs($user)->json('POST', '/articles', $content)->assertJson($content);
+
+ $article = Article::first();
+ $this->assertNotNull($article);
+ $histories = $article->histories;
+ $this->assertNotNull($histories);
+ $this->assertEquals(1, count($histories));
+ $history = $histories[0];
+ $this->assertTrue($history->hasUser());
+ $this->assertNotNull($history->user());
+ $this->assertEquals($user->toJson(), $history->user()->toJson());
+ $this->assertEquals($article->makeHidden('histories')->toJson(), $history->model()->toJson());
+
+ $operations = $user->operations;
+ $this->assertNotNull($operations);
+ $this->assertEquals(1, count($operations));
+ $operation = $operations[0];
+ $this->assertEquals($history->toJson(), $operation->toJson());
+ }
+
+ public function testAnonymous()
+ {
+ $content = ['title' => 'quae et est'];
+ $this->json('POST', '/articles', $content)->assertJson($content);
+
+ $article = Article::first();
+ $this->assertNotNull($article);
+ $histories = $article->histories;
+ $this->assertNotNull($histories);
+ $this->assertEquals(1, count($histories));
+ $history = $histories[0];
+ $this->assertNotTrue($history->hasUser());
+ $this->assertNull($history->user());
+ }
+
+ public function testCustomEvent()
+ {
+ Article::create(['title' => 'maxime fugit saepe']);
+ $article = Article::first();
+ $this->assertNotNull($article);
+ $this->json('GET', '/articles/' . $article->id);
+ $history = History::skip(1)->first();
+ $this->assertNotNull($history);
+ $this->assertEquals($article->id, $history->model_id);
+ $this->assertEquals('Query Article ' . $article->title, $history->message);
+ $this->assertEquals([$article->id], $history->meta);
+ }
+}
\ No newline at end of file
diff --git a/tests/User.php b/tests/User.php
new file mode 100644
index 0000000..3f076a2
--- /dev/null
+++ b/tests/User.php
@@ -0,0 +1,15 @@
+