1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Isswp101\Persimmon\Model; |
4
|
|
|
|
5
|
|
|
use Isswp101\Persimmon\Collection\ICollection; |
6
|
|
|
use Isswp101\Persimmon\DI\Container; |
7
|
|
|
use Isswp101\Persimmon\Exceptions\IllegalCollectionException; |
8
|
|
|
use Isswp101\Persimmon\Exceptions\IllegalModelHashException; |
9
|
|
|
use Isswp101\Persimmon\Exceptions\ModelNotFoundException; |
10
|
|
|
use Isswp101\Persimmon\QueryBuilder\IQueryBuilder; |
11
|
|
|
use Isswp101\Persimmon\Traits\Containerable; |
12
|
|
|
use Isswp101\Persimmon\Traits\Eventable; |
13
|
|
|
use Isswp101\Persimmon\Traits\Timestampable; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* @TODO |
17
|
|
|
* 1. Cache |
18
|
|
|
* 2. Consider columns when searching |
19
|
|
|
* 3. Check __clone() |
20
|
|
|
*/ |
21
|
|
|
abstract class Eloquent implements IEloquent |
22
|
|
|
{ |
23
|
|
|
use Containerable, Timestampable, Eventable; |
24
|
|
|
|
25
|
|
|
protected $primaryKey; |
26
|
|
|
protected $exists = false; |
27
|
|
|
protected $timestamps = false; |
28
|
|
|
protected $cache = false; |
29
|
|
|
|
30
|
|
|
/** @MustBeOverridden */ |
31
|
|
|
const COLLECTION = null; |
32
|
|
|
|
33
|
|
|
const PRIMARY_KEY = 'id'; |
34
|
|
|
const CREATED_AT = 'created_at'; |
35
|
|
|
const UPDATED_AT = 'updated_at'; |
36
|
|
|
|
37
|
|
|
abstract protected static function di(): Container; |
38
|
|
|
|
39
|
|
|
protected static function instantiate(string $primaryKey): IEloquent |
40
|
|
|
{ |
41
|
|
|
$model = new static(); |
42
|
|
|
$model->setPrimaryKey($primaryKey); |
43
|
|
|
return $model; |
44
|
|
|
} |
45
|
|
|
|
46
|
|
|
public function __construct(array $attributes = []) |
47
|
|
|
{ |
48
|
|
|
$this->fill($attributes); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
public function setPrimaryKey(string $key) |
52
|
|
|
{ |
53
|
|
|
$this->primaryKey = $key; |
54
|
|
|
} |
55
|
|
|
|
56
|
|
|
public function getPrimaryKey(): string |
57
|
|
|
{ |
58
|
|
|
if ($this->primaryKey == null) { |
59
|
|
|
$this->setPrimaryKey($this->{static::PRIMARY_KEY}); |
60
|
|
|
} |
61
|
|
|
return $this->primaryKey; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
final public static function getCollection(): string |
65
|
|
|
{ |
66
|
|
|
if (static::COLLECTION == null) { |
67
|
|
|
throw new IllegalCollectionException(); |
68
|
|
|
} |
69
|
|
|
return static::COLLECTION; |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
public function exists(bool $value = null): bool |
73
|
|
|
{ |
74
|
|
|
if ($value != null) { |
75
|
|
|
$this->exists = $value; |
76
|
|
|
} |
77
|
|
|
return $this->exists; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
public static function all(IQueryBuilder $query, callable $callback = null): ICollection |
81
|
|
|
{ |
82
|
|
|
$collection = static::di()->getRepository()->all($query, static::class, |
83
|
|
|
function (IEloquent $model) use ($callback) { |
84
|
|
|
$model->exists(true); |
85
|
|
|
if ($callback != null) { |
86
|
|
|
$callback($model); |
87
|
|
|
} |
88
|
|
|
}); |
89
|
|
|
return $collection; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
public static function find($id, array $columns = []): IEloquent |
93
|
|
|
{ |
94
|
|
|
$di = static::di(); |
95
|
|
|
$model = Eloquent::instantiate($id); |
96
|
|
|
if ($model->shouldUseCache()) { |
97
|
|
|
$cache = $di->getCache()->get($model->getHash()); |
98
|
|
|
$columns = array_diff($columns, $cache->getCachedAttributes()); |
99
|
|
|
if (!$columns && $cache->get() != null) { |
|
|
|
|
100
|
|
|
return $cache->get(); |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
$cache = $di->getCacheRepository()->find($id, static::class, $columns); |
|
|
|
|
105
|
|
|
if ($cache->isReturnable()) { |
|
|
|
|
106
|
|
|
|
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
|
110
|
|
|
$model = $di->getRepository()->find($id, static::class, $columns); |
111
|
|
|
if ($model != null) { |
112
|
|
|
$model->exists = true; |
|
|
|
|
113
|
|
|
if ($model->shouldUseCache()) { |
114
|
|
|
$cache->put($model, $columns); |
115
|
|
|
} |
116
|
|
|
} |
117
|
|
|
return $model; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
public static function findOrFail($id, array $columns = []): IEloquent |
121
|
|
|
{ |
122
|
|
|
$model = static::find($id, $columns); |
123
|
|
|
if ($model == null) { |
124
|
|
|
throw new ModelNotFoundException(get_called_class(), $id); |
125
|
|
|
} |
126
|
|
|
return $model; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
public static function create(array $attributes): IEloquent |
130
|
|
|
{ |
131
|
|
|
$model = new static($attributes); |
132
|
|
|
$model->save(); |
133
|
|
|
return $model; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
public static function destroy($id) |
137
|
|
|
{ |
138
|
|
|
static::findOrFail($id)->delete(); |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
public function save(array $columns = []) |
142
|
|
|
{ |
143
|
|
|
if ($this->saving() === false) { |
144
|
|
|
return; |
145
|
|
|
} |
146
|
|
|
$repository = $this->di()->getRepository(); |
147
|
|
|
if ($this->timestamps) { |
148
|
|
|
$this->updateTimestamps(); |
149
|
|
|
} |
150
|
|
|
if (!$this->exists) { |
151
|
|
|
$repository->insert($this); |
152
|
|
|
} else { |
153
|
|
|
$repository->update($this); |
154
|
|
|
} |
155
|
|
|
$this->exists = true; |
156
|
|
|
if ($this->saved() === false) { |
157
|
|
|
return; |
158
|
|
|
} |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
public function delete() |
162
|
|
|
{ |
163
|
|
|
if ($this->deleting() === false) { |
164
|
|
|
return; |
165
|
|
|
} |
166
|
|
|
$this->di()->getRepository()->delete($this); |
167
|
|
|
$this->exists = false; |
168
|
|
|
if ($this->deleted() === false) { |
169
|
|
|
return; |
170
|
|
|
} |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
public function getHash(): string |
174
|
|
|
{ |
175
|
|
|
if ($this->getPrimaryKey() == null) { |
176
|
|
|
throw new IllegalModelHashException($this); |
177
|
|
|
} |
178
|
|
|
return get_class($this) . '@' . $this->getPrimaryKey(); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
public function shouldUseCache(): bool |
182
|
|
|
{ |
183
|
|
|
return $this->cache; |
184
|
|
|
} |
185
|
|
|
} |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.