forked from kallaspriit/Cassandra-PHP-Client-Library
/
CassandraModel.php
executable file
·579 lines (517 loc) · 14.8 KB
/
CassandraModel.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
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
<?php
/**
* Cassandra-PHP-Client Library (CPCL).
*
* Cassandra PHP-based client library for managing and querying your Cassandra
* cluster. It's a high-level library performing all the rather complex
* low-level lifting and providing a simple to learn and use interface.
*
* Includes ideas and code snippets from PHPCassa project.
*
* Copyright (C) 2011 by Priit Kallas <kallaspriit@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author Priit Kallas <kallaspriit@gmail.com>
* @package Cassandra
* @version 1.0
*/
/**
* Provides an abstraction layer on top of Cassandra where you can extend this
* class where the class name determines the column-family name and public
* members the columns.
*/
class CassandraModel {
/**
* Default connection to use.
*
* @var Cassandra
*/
protected static $_defaultConnection;
/**
* Active connection.
*
* @var Cassandra
*/
protected $_connection;
/**
* Row key
*
* @var string
*/
protected $_key;
/**
* Constructor, optionally sets the row key and connection to use.
*
* If no connection is given, uses the default connection that you should
* set once using CassandraModel::setDefaultConnection().
*
* @param string $key Optional row key if creating a new entry
* @param Cassandra $connection Connection to use instead of default
*/
public function __construct($key = null, Cassandra $connection = null) {
$this->_key = $key;
if ($connection !== null) {
$this->setConnection($connection);
} else {
$this->_connection = self::$_defaultConnection;
}
}
/**
* Static method to get an instance of the class.
*
* Optionally sets the row key and connection to use. If no connection is
* given, uses the default connection that you should set once using
* CassandraModel::setDefaultConnection().
*
* @param string $key Optional row key if creating a new entry
* @param Cassandra $connection Connection to use instead of default
* @return CassandraModel Called class instance
*/
public static function getInstance(
$key = null,
Cassandra $connection = null
) {
$className = get_called_class();
return new $className($key, $connection);
}
/**
* Sets the default Cassandra connection to use.
*
* You will usually call it once during boostrapping so you wouldn't need
* to set it again every time in constructor or the static methods.
*
* @param Cassandra $connection Connection to use by default
*/
public static function setDefaultConnection(Cassandra $connection) {
self::$_defaultConnection = $connection;
}
/**
* Returns the current default connection.
*
* @return Cassandra
*/
public static function getDefaultConnection() {
return self::$_defaultConnection;
}
/**
* Overrides the default connection for current instance.
*
* @param Cassandra $connection Connection to use
*/
public function setConnection(Cassandra $connection) {
$this->_connection = $connection;
}
/**
* Returns current instance Cassandra connection.
*
* @return Cassandra
*/
public function getConnection() {
return $this->_connection;
}
/**
* Populates model with data.
*
* Array keys should match model public members.
*
* @param array $data Data to use
* @return CassandraModel Self for chaining
*/
public function populate(array $data) {
foreach ($data as $key => $value) {
$this->$key = $value;
}
return $this;
}
/**
* Returns the row key, also setting it if key is provided.
*
* @param string $newKey Optioal new key to use
* @return string Active row key
*/
public function key($newKey = null) {
if (isset($newKey)) {
$this->_key = $newKey;
}
return $this->_key;
}
/**
* Returns model data as an associative array.
*
* @return array
*/
public function getData() {
$columns = $this->getSetColumnNames();
$data = array();
foreach ($columns as $column) {
$data[$column] = $this->$column;
}
return $data;
}
/**
* Loads data into the model by row key.
*
* Returns the model instance if entry was found and null otherwise.
*
* @param mixed $rowKey Value of the row key to find item by.
* @param integer $consistency Option override of default consistency level
* @param Cassandra $connection If not set, the default connection is used
* @return CassandraModel|null Model instance or null if not found
*/
public static function load(
$rowKey,
$consistency = null,
Cassandra $connection = null
) {
$columnFamily = self::getColumnFamilyName();
$columns = self::getColumnNames();
if (!isset($connection)) {
$connection = self::$_defaultConnection;
}
if (empty($columns)) {
$columns = null;
}
$supercolumn = null;
$dotPos = mb_strpos($rowKey, '.');
if ($dotPos !== false) {
$supercolumn = mb_substr($rowKey, $dotPos + 1);
$rowKey = mb_substr($rowKey, 0, $dotPos);
}
$data = $connection->cf($columnFamily)->get(
$rowKey,
$columns,
null,
null,
false,
100,
$supercolumn,
$consistency
);
if ($data !== null) {
$instance = self::getInstance();
$instance->populate($data);
$instance->_key = $rowKey;
$instance->setConnection($connection);
return $instance;
} else {
return null;
}
}
/**
* Returns a range of rows.
*
* This is available only when using the order preserving partitioner. Can
* be used to paginate data etc. Returns an iterator that fetches the data
* from the database in chunks.
*
* @param string $startKey Starting key, null to start with first
* @param string $endKey Ending key, null for all
* @param integer $consistency Option override of default consistency level
* @param Cassandra $connection If not set, the default connection is used
* @return CassandraRangeDataIterator Key range iterator
*/
public static function getKeyRange(
$startKey = null,
$endKey = null,
$consistency = null,
Cassandra $connection = null
) {
$columnFamily = self::getColumnFamilyName();
$columns = self::getColumnNames();
if (!isset($connection)) {
$connection = self::$_defaultConnection;
}
return $connection->cf($columnFamily)->getKeyRange(
$startKey,
$endKey,
null,
$columns,
null,
null,
false,
100,
null,
$consistency,
100
);
}
/**
* Returns all columns for given row key.
*
* @param string $rowKey Row key to get data from
* @param Cassandra $connection If not set, the default connection is used
* @return array
*/
public static function getAll($rowKey, Cassandra $connection = null) {
if (!isset($connection)) {
$connection = self::$_defaultConnection;
}
$columnFamily = self::getColumnFamilyName();
return $connection->cf($columnFamily)->getAll($rowKey);
}
/**
* Returns a range of columns for a key.
*
* @param string $key Row key
* @param string $startColumn Starting column, null for first
* @param string $endColumn Ending column, null for last
* @param integer $limit Maximum number of columns to return
* @param integer $consistency Consistency to use, null for default
* @param Cassandra $connection Connection to use
* @return array Columns of data in given range up to given limit items
*/
public static function getColumnRange(
$key,
$startColumn = null,
$endColumn = null,
$limit = 100,
$consistency = null,
Cassandra $connection = null
) {
$columnFamily = self::getColumnFamilyName();
if (!isset($connection)) {
$connection = self::$_defaultConnection;
}
return $connection->cf($columnFamily)->get(
$key,
null,
$startColumn,
$endColumn,
false,
$limit,
null,
$consistency
);
}
/**
* Saves the row into the database.
*
* You can set a new key for the row and also populate the model with data
* if you haven't done so already.
*
* You may also set the consistency if you like it to be something else from
* tge default of Cassandra::CONSISTENCY_ONE
*
* @param string $newKey Optional new row key
* @param array $populate Optionally populate the model with data
* @param integer $consistency Option override of default consistency level
* @return boolean Was saving the entry successful
*/
public function save(
$newKey = null,
array $populate = null,
$consistency = null
) {
if (isset($newKey)) {
$this->_key = $newKey;
}
if (is_array($populate)) {
$this->populate($populate);
}
$columnFamily = self::getColumnFamilyName();
$this->_connection->cf($columnFamily)->set(
$this->_key,
$this->getData(),
$consistency
);
return true;
}
/**
* Inserts a new row into the database.
*
* This is a convenience proxy to creating an instance of the model,
* populating it and calling save().
*
* @param string $key Row key to use
* @param array $data Data to populate the model with
* @param integer $consistency Option override of default consistency level
* @param Cassandra $connection If not set, the default connection is used
* @return boolean Was adding the entry successful
*/
public static function insert(
$key,
array $data,
$consistency = null,
Cassandra $connection = null
) {
return self::getInstance(null, $connection)->save(
$key, $data, $consistency
);
}
/**
* Removes an entry or some columns of it.
*
* If the columns is left to null, the entire row is deleted.
*
* If you already have an instance of the model, use delete().
*
* @param string $key Row key to delete
* @param array $columns Optional columns to delete
* @param integer $consistency Option override of default consistency level
* @param Cassandra $connection If not set, the default connection is used
* @return boolean Was removing the entry successful
*/
public static function remove(
$key,
array $columns = null,
$consistency = null,
Cassandra $connection = null
) {
$model = self::getInstance($key, $connection);
return $model->delete($columns, $consistency);
}
/**
* Removes a supercolumn entry or some columns of it.
*
* If the columns is left to null, the entire row is deleted.
*
* If you already have an instance of the model, use deleteSuper().
*
* @param string $key Row key to delete
* @param string $supercolumn Supercolumn name
* @param array $columns Optional columns to delete
* @param integer $consistency Option override of default consistency level
* @param Cassandra $connection If not set, the default connection is used
* @return boolean Was removing the entry successful
*/
public static function removeSuper(
$key,
$supercolumn,
array $columns = null,
$consistency = null,
Cassandra $connection = null
) {
$model = self::getInstance($key, $connection);
return $model->deleteSuper($supercolumn, $columns, $consistency);
}
/**
* Removes an entry or some columns of it.
*
* If the columns is left to null, the entire row is deleted.
*
* Uses the currently set row key, you can change it with key() method.
*
* You can remove a row by calling remove() statically.
*
* @param array $columns Optional columns to delete
* @param integer $consistency Option override of default consistency level
* @return boolean Was removing the entry successful
*/
public function delete(array $columns = null, $consistency = null) {
$columnFamily = self::getColumnFamilyName();
$this->_connection->cf($columnFamily)->remove(
$this->_key,
$columns,
null,
$consistency
);
return true;
}
/**
* Removes an supercolumn entry or some columns of it.
*
* If the columns is left to null, the entire row is deleted.
*
* Uses the currently set row key, you can change it with key() method.
*
* You can remove a row by calling removeSuper() statically.
*
* @param array $columns Optional columns to delete
* @param integer $consistency Option override of default consistency level
* @return boolean Was removing the entry successful
*/
public function deleteSuper($supercolumn, array $columns = null, $consistency = null) {
$columnFamily = self::getColumnFamilyName();
$this->_connection->cf($columnFamily)->remove(
$this->_key,
$columns,
$supercolumn,
$consistency
);
return true;
}
/**
* Returns the column family name that corresponds to given model class.
*
* The transformation is done in following steps:
* 1. remove "Model" from the end of class name
* 2. add a underscore before every upper-case word except first
* 3. make everything lowercase
*
* So for example, class names to table names:
* - UserModel > user
* - ForumTopicsModel > forum_topics
*
* @return string The table name
*/
public static function getColumnFamilyName() {
$className = get_called_class();
$components = preg_split(
'/([A-Z][^A-Z]*)/',
substr($className, 0, -5),
-1,
PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
);
return mb_strtolower(implode('_', $components));
}
/**
* Returns the column names of the model.
*
* The column names are determined from public class vars.
*
* @return array
*/
public static function getColumnNames() {
return array_keys(
get_class_public_vars(get_called_class())
);
}
/**
* Returns the column names of the model that have been set for current
* object.
*
* The column names are determined from public object vars.
*
* @return array
*/
public function getSetColumnNames() {
return array_keys(get_object_public_vars($this));
}
}
if (!function_exists('get_class_public_vars')) {
/**
* Returns publicly available properties of a class.
*
* @param string $class_name Class name
* @return array Public var names
*/
function get_class_public_vars($class_name) {
return get_class_vars($class_name);
}
}
if (!function_exists('get_object_public_vars')) {
/**
* Returns publicly available properties of an object.
*
* @param object $object Object to probe
* @return array Public var names
*/
function get_object_public_vars($object) {
return get_object_vars($object);
}
}