VersioningScope::addVersion()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 7
nc 1
nop 1
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace ProAI\Versioning;
4
5
use Illuminate\Database\Eloquent\Builder;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, ProAI\Versioning\Builder. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Database\Eloquent\Scope;
8
use Illuminate\Database\Query\JoinClause;
9
use Carbon\Carbon;
10
11
class VersioningScope implements Scope
12
{
13
    /**
14
     * All of the extensions to be added to the builder.
15
     *
16
     * @var array
17
     */
18
    protected $extensions = ['Version', 'AllVersions', 'Moment'];
19
20
    /**
21
     * Apply the scope to a given Eloquent query builder.
22
     *
23
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
24
     * @param  \Illuminate\Database\Eloquent\Model  $model
25
     * @return void
26
     */
27
    public function apply(Builder $builder, Model $model)
28
    {
29
        if (!$this->hasVersionJoin($builder, $model->getVersionTable())) {
0 ignored issues
show
Bug introduced by
It seems like $model->getVersionTable() can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $table of ProAI\Versioning\VersioningScope::hasVersionJoin() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

29
        if (!$this->hasVersionJoin($builder, /** @scrutinizer ignore-type */ $model->getVersionTable())) {
Loading history...
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
        }
35
36
        $this->extend($builder);
37
    }
38
39
    /**
40
     * Remove the scope from the given Eloquent query builder.
41
     *
42
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
43
     * @param  \Illuminate\Database\Eloquent\Model  $model
44
     * @return void
45
     */
46
    public function remove(Builder $builder, Model $model)
47
    {
48
        $table = $model->getVersionTable();
49
50
        $query = $builder->getQuery();
51
52
        $query->joins = collect($query->joins)->reject(function($join) use ($table)
53
        {
54
            return $this->isVersionJoinConstraint($join, $table);
55
        })->values()->all();
56
    }
57
58
    /**
59
     * Extend the query builder with the needed functions.
60
     *
61
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
62
     * @return void
63
     */
64
    public function extend(Builder $builder)
65
    {
66
        foreach ($this->extensions as $extension)
67
        {
68
            $this->{"add{$extension}"}($builder);
69
        }
70
    }
71
72
    /**
73
     * Add the version extension to the builder.
74
     *
75
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
76
     * @return void
77
     */
78
    protected function addVersion(Builder $builder)
79
    {
80
        $builder->macro('version', function(Builder $builder, $version) {
81
            $model = $builder->getModel();
82
83
            $this->remove($builder, $builder->getModel());
84
85
            $builder->join($model->getVersionTable(), function($join) use ($model, $version) {
86
                $join->on($model->getQualifiedKeyName(), '=', $model->getQualifiedVersionKeyName());
87
                $join->where($model->getQualifiedVersionColumn(), '=', $version);
88
            });
89
90
            return $builder;
91
        });
92
    }
93
94
    /**
95
     * Add the allVersions extension to the builder.
96
     *
97
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
98
     * @return void
99
     */
100
    protected function addAllVersions(Builder $builder)
101
    {
102
        $builder->macro('allVersions', function(Builder $builder) {
103
            $model = $builder->getModel();
104
105
            $this->remove($builder, $builder->getModel());
106
107
            $builder->join($model->getVersionTable(), function($join) use ($model) {
108
                $join->on($model->getQualifiedKeyName(), '=', $model->getQualifiedVersionKeyName());
109
            });
110
111
            return $builder;
112
        });
113
    }
114
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
137
    /**
138
     * Determine if the given join clause is a version constraint.
139
     *
140
     * @param  \Illuminate\Database\Query\JoinClause   $join
141
     * @param  string  $column
142
     * @return bool
143
     */
144
    protected function isVersionJoinConstraint(JoinClause $join, $table)
145
    {
146
        return $join->type == 'inner' && $join->table == $table;
147
    }
148
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
    }
160
}
161