465 lines
9.9 KiB
PHP
465 lines
9.9 KiB
PHP
<?php
|
|
|
|
namespace BaoPham\DynamoDb;
|
|
|
|
use Exception;
|
|
use DateTime;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Support\Arr;
|
|
|
|
/**
|
|
* Class DynamoDbModel.
|
|
*/
|
|
abstract class DynamoDbModel extends Model
|
|
{
|
|
/**
|
|
* Always set this to false since DynamoDb does not support incremental Id.
|
|
*
|
|
* @var bool
|
|
*/
|
|
public $incrementing = false;
|
|
|
|
/**
|
|
* @var \BaoPham\DynamoDb\DynamoDbClientInterface
|
|
*/
|
|
protected static $dynamoDb;
|
|
|
|
/**
|
|
* @deprecated
|
|
* @var \Aws\DynamoDb\Marshaler
|
|
*/
|
|
protected $marshaler;
|
|
|
|
/**
|
|
* @deprecated
|
|
* @var \BaoPham\DynamoDb\EmptyAttributeFilter
|
|
*/
|
|
protected $attributeFilter;
|
|
|
|
/**
|
|
* Indexes.
|
|
* [
|
|
* '<simple_index_name>' => [
|
|
* 'hash' => '<index_key>'
|
|
* ],
|
|
* '<composite_index_name>' => [
|
|
* 'hash' => '<index_hash_key>',
|
|
* 'range' => '<index_range_key>'
|
|
* ],
|
|
* ]
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $dynamoDbIndexKeys = [];
|
|
|
|
/**
|
|
* Array of your composite key.
|
|
* ['<hash>', '<range>']
|
|
*
|
|
* @var array
|
|
*/
|
|
protected $compositeKey = [];
|
|
|
|
/**
|
|
* Default Date format
|
|
* ISO 8601 Compliant
|
|
*/
|
|
protected $dateFormat = DateTime::ATOM;
|
|
|
|
|
|
public function __construct(array $attributes = [])
|
|
{
|
|
$this->bootIfNotBooted();
|
|
|
|
$this->syncOriginal();
|
|
|
|
$this->fill($attributes);
|
|
|
|
$this->setupDynamoDb();
|
|
}
|
|
|
|
/**
|
|
* Get the DynamoDbClient service that is being used by the models.
|
|
*
|
|
* @return DynamoDbClientInterface
|
|
*/
|
|
public static function getDynamoDbClientService()
|
|
{
|
|
return static::$dynamoDb;
|
|
}
|
|
|
|
/**
|
|
* Set the DynamoDbClient used by models.
|
|
*
|
|
* @param DynamoDbClientInterface $dynamoDb
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function setDynamoDbClientService(DynamoDbClientInterface $dynamoDb)
|
|
{
|
|
static::$dynamoDb = $dynamoDb;
|
|
}
|
|
|
|
/**
|
|
* Unset the DynamoDbClient service for models.
|
|
*
|
|
* @return void
|
|
*/
|
|
public static function unsetDynamoDbClientService()
|
|
{
|
|
static::$dynamoDb = null;
|
|
}
|
|
|
|
protected function setupDynamoDb()
|
|
{
|
|
$this->marshaler = static::$dynamoDb->getMarshaler();
|
|
$this->attributeFilter = static::$dynamoDb->getAttributeFilter();
|
|
}
|
|
|
|
public function newCollection(array $models = [], $index = null)
|
|
{
|
|
return new DynamoDbCollection($models, $index);
|
|
}
|
|
|
|
public function save(array $options = [])
|
|
{
|
|
$create = !$this->exists;
|
|
|
|
if ($this->fireModelEvent('saving') === false) {
|
|
return false;
|
|
}
|
|
|
|
if ($create && $this->fireModelEvent('creating') === false) {
|
|
return false;
|
|
}
|
|
|
|
if (!$create && $this->fireModelEvent('updating') === false) {
|
|
return false;
|
|
}
|
|
|
|
if ($this->usesTimestamps()) {
|
|
$this->updateTimestamps();
|
|
}
|
|
|
|
$saved = $this->newQuery()->save();
|
|
|
|
if (!$saved) {
|
|
return $saved;
|
|
}
|
|
|
|
$this->exists = true;
|
|
$this->wasRecentlyCreated = $create;
|
|
$this->fireModelEvent($create ? 'created' : 'updated', false);
|
|
|
|
$this->finishSave($options);
|
|
|
|
return $saved;
|
|
}
|
|
|
|
/**
|
|
* Saves the model to DynamoDb asynchronously and returns a promise
|
|
* @param array $options
|
|
* @return bool|\GuzzleHttp\Promise\Promise
|
|
*/
|
|
public function saveAsync(array $options = [])
|
|
{
|
|
$create = !$this->exists;
|
|
|
|
if ($this->fireModelEvent('saving') === false) {
|
|
return false;
|
|
}
|
|
|
|
if ($create && $this->fireModelEvent('creating') === false) {
|
|
return false;
|
|
}
|
|
|
|
if (!$create && $this->fireModelEvent('updating') === false) {
|
|
return false;
|
|
}
|
|
|
|
if ($this->usesTimestamps()) {
|
|
$this->updateTimestamps();
|
|
}
|
|
|
|
$savePromise = $this->newQuery()->saveAsync();
|
|
|
|
$savePromise->then(function ($result) use ($create, $options) {
|
|
if (Arr::get($result, '@metadata.statusCode') === 200) {
|
|
$this->exists = true;
|
|
$this->wasRecentlyCreated = $create;
|
|
$this->fireModelEvent($create ? 'created' : 'updated', false);
|
|
|
|
$this->finishSave($options);
|
|
}
|
|
});
|
|
|
|
return $savePromise;
|
|
}
|
|
|
|
public function update(array $attributes = [], array $options = [])
|
|
{
|
|
return $this->fill($attributes)->save();
|
|
}
|
|
|
|
public function updateAsync(array $attributes = [], array $options = [])
|
|
{
|
|
return $this->fill($attributes)->saveAsync($options);
|
|
}
|
|
|
|
public static function create(array $attributes = [])
|
|
{
|
|
$model = new static;
|
|
|
|
$model->fill($attributes)->save();
|
|
|
|
return $model;
|
|
}
|
|
|
|
public function delete()
|
|
{
|
|
if (is_null($this->getKeyName())) {
|
|
throw new Exception('No primary key defined on model.');
|
|
}
|
|
|
|
if ($this->exists) {
|
|
if ($this->fireModelEvent('deleting') === false) {
|
|
return false;
|
|
}
|
|
|
|
$this->exists = false;
|
|
|
|
$success = $this->newQuery()->delete();
|
|
|
|
if ($success) {
|
|
$this->fireModelEvent('deleted', false);
|
|
}
|
|
|
|
return $success;
|
|
}
|
|
}
|
|
|
|
public function deleteAsync()
|
|
{
|
|
if (is_null($this->getKeyName())) {
|
|
throw new Exception('No primary key defined on model.');
|
|
}
|
|
|
|
if ($this->exists) {
|
|
if ($this->fireModelEvent('deleting') === false) {
|
|
return false;
|
|
}
|
|
|
|
$this->exists = false;
|
|
|
|
$deletePromise = $this->newQuery()->deleteAsync();
|
|
|
|
$deletePromise->then(function () {
|
|
$this->fireModelEvent('deleted', false);
|
|
});
|
|
|
|
return $deletePromise;
|
|
}
|
|
}
|
|
|
|
public static function all($columns = [])
|
|
{
|
|
$instance = new static;
|
|
|
|
return $instance->newQuery()->get($columns);
|
|
}
|
|
|
|
public function refresh()
|
|
{
|
|
if (! $this->exists) {
|
|
return $this;
|
|
}
|
|
|
|
$query = $this->newQuery();
|
|
|
|
$refreshed = $query->find($this->getKeys());
|
|
|
|
$this->setRawAttributes($refreshed->toArray());
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return DynamoDbQueryBuilder
|
|
*/
|
|
public function newQuery()
|
|
{
|
|
$builder = new DynamoDbQueryBuilder($this);
|
|
|
|
foreach ($this->getGlobalScopes() as $identifier => $scope) {
|
|
$builder->withGlobalScope($identifier, $scope);
|
|
}
|
|
|
|
return $builder;
|
|
}
|
|
|
|
public function hasCompositeKey()
|
|
{
|
|
return !empty($this->compositeKey);
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
* @param $item
|
|
* @return array
|
|
*/
|
|
public function marshalItem($item)
|
|
{
|
|
return $this->marshaler->marshalItem($item);
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
* @param $value
|
|
* @return array
|
|
*/
|
|
public function marshalValue($value)
|
|
{
|
|
return $this->marshaler->marshalValue($value);
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
* @param $item
|
|
* @return array|\stdClass
|
|
*/
|
|
public function unmarshalItem($item)
|
|
{
|
|
return $this->marshaler->unmarshalItem($item);
|
|
}
|
|
|
|
public function setId($id)
|
|
{
|
|
if (!is_array($id)) {
|
|
$this->setAttribute($this->getKeyName(), $id);
|
|
|
|
return $this;
|
|
}
|
|
|
|
foreach ($id as $keyName => $value) {
|
|
$this->setAttribute($keyName, $value);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return \Aws\DynamoDb\DynamoDbClient
|
|
*/
|
|
public function getClient()
|
|
{
|
|
return static::$dynamoDb->getClient($this->getConnectionName());
|
|
}
|
|
|
|
/**
|
|
* Get the value of the model's primary key.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function getKey()
|
|
{
|
|
return $this->getAttribute($this->getKeyName());
|
|
}
|
|
|
|
/**
|
|
* Get the value of the model's primary / composite key.
|
|
* Use this if you always want the key values in associative array form.
|
|
*
|
|
* @return array
|
|
*
|
|
* ['id' => 'foo']
|
|
*
|
|
* or
|
|
*
|
|
* ['id' => 'foo', 'id2' => 'bar']
|
|
*/
|
|
public function getKeys()
|
|
{
|
|
if ($this->hasCompositeKey()) {
|
|
$key = [];
|
|
|
|
foreach ($this->compositeKey as $name) {
|
|
$key[$name] = $this->getAttribute($name);
|
|
}
|
|
|
|
return $key;
|
|
}
|
|
|
|
$name = $this->getKeyName();
|
|
|
|
return [$name => $this->getAttribute($name)];
|
|
}
|
|
|
|
/**
|
|
* Get the primary key for the model.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getKeyName()
|
|
{
|
|
return $this->primaryKey;
|
|
}
|
|
|
|
/**
|
|
* Get the primary/composite key for the model.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getKeyNames()
|
|
{
|
|
return $this->hasCompositeKey() ? $this->compositeKey : [$this->primaryKey];
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getDynamoDbIndexKeys()
|
|
{
|
|
return $this->dynamoDbIndexKeys;
|
|
}
|
|
|
|
/**
|
|
* @param array $dynamoDbIndexKeys
|
|
*/
|
|
public function setDynamoDbIndexKeys($dynamoDbIndexKeys)
|
|
{
|
|
$this->dynamoDbIndexKeys = $dynamoDbIndexKeys;
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
* @return \Aws\DynamoDb\Marshaler
|
|
*/
|
|
public function getMarshaler()
|
|
{
|
|
return $this->marshaler;
|
|
}
|
|
|
|
/**
|
|
* Remove non-serializable properties when serializing.
|
|
*
|
|
* @return array
|
|
*/
|
|
public function __sleep()
|
|
{
|
|
return array_keys(
|
|
Arr::except(get_object_vars($this), ['marshaler', 'attributeFilter'])
|
|
);
|
|
}
|
|
|
|
/**
|
|
* When a model is being unserialized, check if it needs to be booted and setup DynamoDB.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function __wakeup()
|
|
{
|
|
parent::__wakeup();
|
|
$this->setupDynamoDb();
|
|
}
|
|
}
|