-
Notifications
You must be signed in to change notification settings - Fork 26
/
ActiveRecord.php
301 lines (268 loc) · 7.12 KB
/
ActiveRecord.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
<?php
namespace Mongolid;
use BadMethodCallException;
use Mongolid\Container\Ioc;
use Mongolid\DataMapper\DataMapper;
use Mongolid\Model\Attributes;
use Mongolid\Model\Relations;
use Mongolid\Serializer\Serializer;
use Serializable;
/**
* The Mongolid\ActiveRecord base class will ensure to enable your entity to
* have methods to interact with the database. It means that 'save', 'insert',
* 'update', 'where', 'first' and 'all' are available within every instance.
* The Mongolid\Schema that describes the entity will be generated on the go
* based on the $fields.
*
* @package Mongolid
*/
abstract class ActiveRecord implements Serializable
{
use Attributes, Relations;
/**
* Name of the collection where this kind of Entity is going to be saved or
* retrieved from
*
* @var string
*/
protected $collection = null;
/**
* @see https://docs.mongodb.com/manual/reference/write-concern/
* @var integer
*/
protected $writeConcern = 1;
/**
* Describes the Schema fields of the model. Optionally you can set it to
* the name of a Schema class to be used.
*
* @see Mongolid\Schema::$fields
* @var string|string[]
*/
protected $fields = [
'_id' => 'objectId',
'created_at' => 'createdAtTimestamp',
'updated_at' => 'updatedAtTimestamp'
];
/**
* The $dynamic property tells if the object will accept additional fields
* that are not specified in the $fields property. This is useful if you
* does not have a strict document format or if you want to take full
* advantage of the "schemaless" nature of MongoDB.
*
* @var boolean
*/
public $dynamic = true;
/**
* Saves this object into database
*
* @return boolean Success
*/
public function save()
{
return $this->execute('save');
}
/**
* Insert this object into database
*
* @return boolean Success
*/
public function insert()
{
return $this->execute('insert');
}
/**
* Updates this object in database
*
* @return boolean Success
*/
public function update()
{
return $this->execute('update');
}
/**
* Deletes this object in database
*
* @return boolean Success
*/
public function delete()
{
return $this->execute('delete');
}
/**
* Gets a cursor of this kind of entities that matches the query from the
* database
*
* @param array $query MongoDB selection criteria.
*
* @return \Mongolid\Cursor\Cursor
*/
public static function where(array $query = [])
{
return Ioc::make(get_called_class())
->getDataMapper()->where($query);
}
/**
* Gets a cursor of this kind of entities from the database
*
* @return \Mongolid\Cursor\Cursor
*/
public static function all()
{
return Ioc::make(get_called_class())
->getDataMapper()->all();
}
/**
* Gets the first entity of this kind that matches the query
*
* @param mixed $query MongoDB selection criteria.
*
* @return ActiveRecord
*/
public static function first($query = [])
{
return Ioc::make(get_called_class())
->getDataMapper()->first($query);
}
/**
* Handle dynamic method calls into the model.
*
* @codeCoverageIgnore
*
* @param mixed $method Name of the method that is being called.
* @param mixed $parameters Parameters of $method.
*
* @throws BadMethodCallException In case of invalid methods be called.
*
* @return mixed
*/
public function __call($method, $parameters)
{
$value = $parameters[0] ?? null;
// Alias to attach
if ('attachTo' == substr($method, 0, 8)) {
$field = strtolower(substr($method, 8));
return $this->attach($field, $value);
}
// Alias to embed
if ('embedTo' == substr($method, 0, 7)) {
$field = strtolower(substr($method, 7));
return $this->embed($field, $value);
}
throw new BadMethodCallException(
sprintf(
'The following method can not be reached or does not exist: %s@%s',
get_class($this),
$method
)
);
}
/**
* Returns a DataMapper configured with the Schema and collection described
* in this entity
*
* @return DataMapper
*/
public function getDataMapper()
{
$dataMapper = Ioc::make(DataMapper::class);
$dataMapper->schema = $this->getSchema();
return $dataMapper;
}
/**
* Getter for the $collection attribute.
*
* @return string
*/
public function getCollectionName()
{
return $this->collection;
}
/**
* {@inheritdoc}
*
* @return string
*/
public function serialize()
{
return Ioc::make(Serializer::class)->serialize($this->getAttributes());
}
/**
* {@inheritdoc}
*
* @param mixed $data Serialized string to parse.
*
* @return void
*/
public function unserialize($data)
{
$this->fill(Ioc::make(Serializer::class)->unserialize($data), true);
}
/**
* Getter for $writeConcern variable
*
* @return mixed
*/
public function getWriteConcern()
{
return $this->writeConcern;
}
/**
* Setter for $writeConcern variable
*
* @param mixed $writeConcern Level of write concern to the transation.
*
* @return void
*/
public function setWriteConcern($writeConcern)
{
$this->writeConcern = $writeConcern;
}
/**
* Returns a Schema object that describes this Entity in MongoDB
*
* @return Schema
*/
protected function getSchema(): Schema
{
if ($schema = $this->instantiateSchemaInFields()) {
return $schema;
}
$schema = new DynamicSchema;
$schema->entityClass = get_class($this);
$schema->fields = $this->fields;
$schema->dynamic = $this->dynamic;
$schema->collection = $this->getCollectionName();
return $schema;
}
/**
* Will check if the current value of $fields property is the name of a
* Schema class and instantiate it if possible.
*
* @return Schema|null
*/
protected function instantiateSchemaInFields()
{
if (is_string($this->fields)) {
if (is_subclass_of($instance = Ioc::make($this->fields), Schema::class)) {
return $instance;
}
}
}
/**
* Performs the given action into database
*
* @param string $action Datamapper function to execute.
*
* @return boolean
*/
protected function execute(string $action)
{
if (! $this->getCollectionName()) {
return false;
}
$options = [
'writeConcern' => $this->getWriteConcern(),
];
return $this->getDataMapper()->$action($this, $options);
}
}