Skip to content

Commit f8e2ef4

Browse files
author
Peter Klooster
authored
Merge pull request #19 from crashkonijn/master
Added tests, fixed a couple of bugs, added new moment feature.
2 parents a99999c + cb54634 commit f8e2ef4

17 files changed

+834
-12
lines changed

.gitignore

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.idea
2+
/vendor
3+
/coverage
4+
composer.lock

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ By default the query builder will fetch the latest version (e. g. `User::find(1)
9090

9191
* `allVersions()` returns all versions of the queried items<br>Example: `User::allVersions()->get()` will return all versions of all users
9292

93+
* `moment(Carbon)` returns a specific version, closest but lower than the input date<br>Example: `User::moment(Carbon::now()->subWeek()->find(1)` will return the version at that point in time.
94+
9395
#### Create, update and delete records
9496

9597
All these operations can be performed normally. The package will automatically generate a version 1 on create, the next version on update and will remove all versions on delete.

composer.json

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
{
22
"name": "proai/eloquent-versioning",
33
"description": "An extension for the Eloquent ORM to support versioning.",
4-
"keywords": ["laravel","orm","eloquent","revision","version","versioned","versioning","audit"],
4+
"keywords": [
5+
"laravel",
6+
"orm",
7+
"eloquent",
8+
"revision",
9+
"version",
10+
"versioned",
11+
"versioning",
12+
"audit",
13+
"audits"
14+
],
515
"homepage": "http://github.com/ProAI/eloquent-versioning",
616
"license": "MIT",
717
"authors": [
@@ -11,14 +21,25 @@
1121
}
1222
],
1323
"require": {
14-
"php": ">=5.4.0",
24+
"php": ">=7.1",
1525
"illuminate/database": "5.*"
1626
},
27+
"require-dev": {
28+
"phpunit/phpunit": "^7.0",
29+
"mockery/mockery": "^1.0",
30+
"orchestra/testbench": "^3.6",
31+
"orchestra/database": "^3.6"
32+
},
1733
"autoload": {
1834
"psr-4": {
1935
"ProAI\\Versioning\\": "src/"
2036
}
2137
},
38+
"autoload-dev": {
39+
"psr-4": {
40+
"ProAI\\Versioning\\Tests\\": "tests/"
41+
}
42+
},
2243
"extra": {
2344
"branch-alias": {
2445
"dev-master": "1.0-dev"

phpunit.xml

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit backupGlobals="false"
3+
backupStaticAttributes="false"
4+
bootstrap="vendor/autoload.php"
5+
colors="true"
6+
convertErrorsToExceptions="true"
7+
convertNoticesToExceptions="true"
8+
convertWarningsToExceptions="true"
9+
processIsolation="false"
10+
stopOnError="false"
11+
stopOnFailure="false"
12+
syntaxCheck="true"
13+
verbose="true"
14+
>
15+
<testsuites>
16+
<testsuite name="Versioning Test Suite">
17+
<directory suffix="Test.php">./tests</directory>
18+
</testsuite>
19+
</testsuites>
20+
<filter>
21+
<whitelist processUncoveredFilesFromWhitelist="true">
22+
<directory suffix=".php">./src</directory>
23+
</whitelist>
24+
</filter>
25+
</phpunit>

src/BuilderTrait.php

+11-4
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@ public function insert(array $values)
4343

4444
// set version, ref_id and latest_version
4545
$values[$this->model->getLatestVersionColumn()] = 1;
46-
$versionValues[$this->model->getVersionKeyName()] = $this->model->getKey();
47-
$versionValues[$this->model->getVersionColumn()] = 1;
4846

4947
// insert main table record
50-
if (! $this->query->insert($values)) {
48+
if (! $id = $this->query->insertGetId($values)) {
5149
return false;
5250
}
5351

52+
$versionValues[$this->model->getVersionKeyName()] = $id;
53+
$versionValues[$this->model->getVersionColumn()] = 1;
54+
5455
// insert version table record
5556
$db = $this->model->getConnection();
5657
return $db->table($this->model->getVersionTable())->insert($versionValues);
@@ -87,6 +88,9 @@ public function insertGetId(array $values, $sequence = null)
8788
return false;
8889
}
8990

91+
// fill the latest version value
92+
$this->model->{$this->model->getLatestVersionColumn()} = 1;
93+
9094
return $id;
9195
}
9296

@@ -126,14 +130,17 @@ public function update(array $values)
126130

127131
// set version and ref_id
128132
$recordVersionValues[$this->model->getVersionKeyName()] = $record->{$this->model->getKeyName()};
129-
$recordVersionValues[$this->model->getVersionColumn()] = $record->{$this->model->getVersionColumn()}+1;
133+
$recordVersionValues[$this->model->getVersionColumn()] = $record->{$this->model->getLatestVersionColumn()}+1;
130134

131135
// insert new version
132136
if(! $db->table($this->model->getVersionTable())->insert($recordVersionValues)) {
133137
return false;
134138
}
135139
}
136140

141+
// fill the latest version value
142+
$this->model->{$this->model->getLatestVersionColumn()} += 1;
143+
137144
return true;
138145
}
139146

src/SoftDeletes.php

-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,4 @@ public function getQualifiedDeletedAtColumn()
2323

2424
return $this->getTable().'.'.$deletedAt;
2525
}
26-
2726
}

src/VersioningScope.php

+41-5
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use Illuminate\Database\Eloquent\Model;
77
use Illuminate\Database\Eloquent\Scope;
88
use Illuminate\Database\Query\JoinClause;
9+
use Carbon\Carbon;
910

1011
class VersioningScope implements Scope
1112
{
@@ -14,7 +15,7 @@ class VersioningScope implements Scope
1415
*
1516
* @var array
1617
*/
17-
protected $extensions = ['Version', 'AllVersions'];
18+
protected $extensions = ['Version', 'AllVersions', 'Moment'];
1819

1920
/**
2021
* Apply the scope to a given Eloquent query builder.
@@ -25,10 +26,12 @@ class VersioningScope implements Scope
2526
*/
2627
public function apply(Builder $builder, Model $model)
2728
{
28-
$builder->join($model->getVersionTable(), function($join) use ($model) {
29-
$join->on($model->getQualifiedKeyName(), '=', $model->getQualifiedVersionKeyName());
30-
$join->on($model->getQualifiedVersionColumn(), '=', $model->getQualifiedLatestVersionColumn());
31-
});
29+
if (!$this->hasVersionJoin($builder, $model->getVersionTable())) {
30+
$builder->join($model->getVersionTable(), function($join) use ($model) {
31+
$join->on($model->getQualifiedKeyName(), '=', $model->getQualifiedVersionKeyName());
32+
$join->on($model->getQualifiedVersionColumn(), '=', $model->getQualifiedLatestVersionColumn());
33+
});
34+
}
3235

3336
$this->extend($builder);
3437
}
@@ -109,6 +112,28 @@ protected function addAllVersions(Builder $builder)
109112
});
110113
}
111114

115+
/**
116+
* Add the moment extension to the builder.
117+
*
118+
* @param \Illuminate\Database\Eloquent\Builder $builder
119+
* @return void
120+
*/
121+
protected function addMoment(Builder $builder)
122+
{
123+
$builder->macro('moment', function(Builder $builder, Carbon $moment) {
124+
$model = $builder->getModel();
125+
126+
$this->remove($builder, $builder->getModel());
127+
128+
$builder->join($model->getVersionTable(), function($join) use ($model, $moment) {
129+
$join->on($model->getQualifiedKeyName(), '=', $model->getQualifiedVersionKeyName());
130+
$join->where('updated_at', '<=', $moment)->orderBy('updated_at', 'desc')->limit(1);
131+
})->orderBy('updated_at', 'desc')->limit(1);
132+
133+
return $builder;
134+
});
135+
}
136+
112137
/**
113138
* Determine if the given join clause is a version constraint.
114139
*
@@ -121,4 +146,15 @@ protected function isVersionJoinConstraint(JoinClause $join, $table)
121146
return $join->type == 'inner' && $join->table == $table;
122147
}
123148

149+
/**
150+
* Determine if the given builder contains a join with the given table
151+
*
152+
* @param Builder $builder
153+
* @param string $table
154+
* @return bool
155+
*/
156+
protected function hasVersionJoin(Builder $builder, string $table)
157+
{
158+
return collect($builder->getQuery()->joins)->pluck('table')->contains($table);
159+
}
124160
}

tests/Models/Post.php

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace ProAI\Versioning\Tests\Models;
4+
5+
use Illuminate\Foundation\Auth\User as Authenticatable;
6+
7+
use ProAI\Versioning\Versionable;
8+
use ProAI\Versioning\SoftDeletes;
9+
10+
class Post extends Authenticatable
11+
{
12+
use Versionable, SoftDeletes;
13+
14+
/**
15+
* The attributes that are mass assignable.
16+
*
17+
* @var array
18+
*/
19+
protected $fillable = [
20+
'title', 'content'
21+
];
22+
23+
public $timestamps = true;
24+
25+
public $versioned = ['content', 'updated_at'];
26+
}

tests/Models/User.php

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace ProAI\Versioning\Tests\Models;
4+
5+
use Illuminate\Foundation\Auth\User as Authenticatable;
6+
7+
use ProAI\Versioning\Versionable;
8+
use ProAI\Versioning\SoftDeletes;
9+
10+
class User extends Authenticatable
11+
{
12+
use Versionable, SoftDeletes;
13+
14+
/**
15+
* The attributes that are mass assignable.
16+
*
17+
* @var array
18+
*/
19+
protected $fillable = [
20+
'email', 'username', 'city', 'latest_version',
21+
];
22+
23+
/**
24+
* The attributes that should be hidden for arrays.
25+
*
26+
* @var array
27+
*/
28+
protected $hidden = [
29+
'password', 'remember_token',
30+
];
31+
32+
public $timestamps = true;
33+
34+
public $versioned = ['email', 'city', 'updated_at', 'deleted_at'];
35+
}

tests/TestCase.php

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
namespace ProAI\Versioning\Tests;
4+
5+
use Orchestra\Database\ConsoleServiceProvider;
6+
use Orchestra\Testbench\TestCase as BaseTestCase;
7+
8+
class TestCase extends BaseTestCase
9+
{
10+
/**
11+
* {@inheritdoc}
12+
*/
13+
protected function getEnvironmentSetUp($app)
14+
{
15+
// Database
16+
$app['config']->set('database.default', 'testing');
17+
$app['config']->set('database.connections.testing', [
18+
'driver' => 'sqlite',
19+
'database' => ':memory:',
20+
'prefix' => '',
21+
]);
22+
}
23+
24+
/**
25+
* {@inheritdoc}
26+
*/
27+
public function setUp()
28+
{
29+
parent::setUp();
30+
$this->loadMigrationsFrom(__DIR__.'/database/migrations');
31+
$this->withFactories(__DIR__.'/database/factories');
32+
}
33+
}

0 commit comments

Comments
 (0)