diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d12bb97 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# phpstorm project files +.idea \ No newline at end of file diff --git a/ActionColumn.php b/ActionColumn.php new file mode 100644 index 0000000..0a10683 --- /dev/null +++ b/ActionColumn.php @@ -0,0 +1,80 @@ + 'edit', + 'icon' => 'pencil', + 'class' => 'btn-primary', + 'label' => 'Edit', + ], + [ + 'url' => 'delete', + 'icon' => 'trash-o', + 'class' => 'btn-danger', + 'label' => 'Delete', + ], + ]; + /** + * @var string the ID of the controller that should handle the actions specified here. + * If not set, it will use the currently active controller. This property is mainly used by + * [[urlCreator]] to create URLs for different actions. The value of this property will be prefixed + * to each action name to form the route of the action. + */ + public $controller; + /** + * @var callable a callback that creates a button URL using the specified model information. + * The signature of the callback should be the same as that of [[createUrl()]]. + * If this property is not set, button URLs will be created using [[createUrl()]]. + */ + public $urlCreator; + + /** + * Creates a URL for the given action and model. + * This method is called for each button and each row. + * @param string $action the button name (or action ID) + * @param \yii\db\ActiveRecord $model the data model + * @param mixed $key the key associated with the data model + * @param integer $index the current row index + * @return string the created URL + */ + public function createUrl($action, $model, $key, $index) + { + if ($this->urlCreator instanceof Closure) { + return call_user_func($this->urlCreator, $action, $model, $key, $index); + } else { + $params = is_array($key) ? $key : ['id' => (string) $key]; + $params[0] = $this->controller ? $this->controller . '/' . $action : $action; + + return Url::toRoute($params); + } + } + + + protected function renderDataCellContent($model, $key, $index) + { + $data = ''; + foreach ($this->buttons as $button) { + $data .= Html::a( + Icon::show($button['icon']).'  '.$button['label'], + $url = $this->createUrl($button['url'], $model, $key, $index), + [ + 'data-pjax' => 0, + 'class' => 'btn btn-xs '.$button['class'] + ] + ) . ' '; + } + + return $data; + } +} diff --git a/ArangoDbConnection.php b/ArangoDbConnection.php new file mode 100644 index 0000000..f7e8061 --- /dev/null +++ b/ArangoDbConnection.php @@ -0,0 +1,76 @@ + 'tcp://127.0.0.1:8529', + // authorization type to use (currently supported: 'Basic') + ArangoConnectionOptions::OPTION_AUTH_TYPE => 'Basic', + // user for basic authorization + ArangoConnectionOptions::OPTION_AUTH_USER => 'root', + // password for basic authorization + ArangoConnectionOptions::OPTION_AUTH_PASSWD => '', + // connection persistence on server. can use either 'Close' (one-time connections) or 'Keep-Alive' (re-used connections) + ArangoConnectionOptions::OPTION_CONNECTION => 'Close', + // connect timeout in seconds + ArangoConnectionOptions::OPTION_TIMEOUT => 3, + // whether or not to reconnect when a keep-alive connection has timed out on server + ArangoConnectionOptions::OPTION_RECONNECT => true, + // optionally create new collections when inserting documents + ArangoConnectionOptions::OPTION_CREATE => true, + // optionally create new collections when inserting documents + ArangoConnectionOptions::OPTION_UPDATE_POLICY => ArangoUpdatePolicy::LAST, + ]; + + private $_collectionHandler = null; + private $_documentHandler = null; + + public function __construct($config=[]) + { + parent::__construct($config); + } + + public function init() + { + parent::init(); + + $this->_connection = new ArangoConnection($this->connectionOptions); + $this->_collectionHandler = new \triagens\ArangoDb\CollectionHandler($this->_connection); + $this->_documentHandler = new \triagens\ArangoDb\DocumentHandler($this->_connection); + } + + public function getDocument($collection, $id) { + return $this->documentHandler()->get($collection, $id); + } + + public function documentHandler() { + return $this->_documentHandler; + } + + public function statement($options=[]) { + return new Statement($this->_connection, $options); + } + + public function collectionHandler() { + return $this->_collectionHandler; + } +} \ No newline at end of file diff --git a/ArangoModel.php b/ArangoModel.php new file mode 100644 index 0000000..fcc0fcf --- /dev/null +++ b/ArangoModel.php @@ -0,0 +1,100 @@ +setDocument(Yii::$app->arango->getDocument(static::class_to_collection(get_called_class()), $id)) + ->setIsNewRecord(false); + + return $model; + } + + /** + * @todo функция должна возвращать true/false в зависимости от результата + * Но аранга возвращает различный тип данных. Надо написать код + * + */ + public function save() + { + if ($this->_isNewRecord) { + // добавляем запись + $this->_doc = Document::createFromArray($this->getAttributes()); + + return intval(Yii::$app->arango->documentHandler()->add(static::class_to_collection(get_called_class()), $this->_doc)) > 0; + } else { + // патчим! + $doc_attributes = array_keys($this->_doc->getAll()); + + $attributes = $this->getAttributes(); + foreach ($attributes as $k=>$v) { + $this->_doc->set($k, $v); + unset($doc_attributes[$k]); + } + foreach ($doc_attributes as $key) { + if ($key != '_key') + unset($this->_doc->$key); + } + return Yii::$app->arango->documentHandler()->update($this->_doc); + } + } + + private static function class_to_collection($class) + { + $parts = explode("\\", $class); + return end($parts); + } + private static function id_to_int($class) + { + $parts = explode("/", $class); + return end($parts); + } + + public function setIsNewRecord($state) + { + $this->_isNewRecord = $state; + return $this; + } + + public function setDocument($doc) + { + $this->_doc = $doc; + $all = $this->_doc->getAll(); + $this->_id = $this->_doc->getInternalId(); + $this->setAttributes($all, false); + + return $this; + } + + public function delete() + { + + Yii::$app->arango->documentHandler()->deleteById( + static::class_to_collection(get_called_class()), + static::id_to_int($this->_doc->getInternalId()) + ); + } +} \ No newline at end of file diff --git a/ArangoProvider.php b/ArangoProvider.php new file mode 100644 index 0000000..b0b39d2 --- /dev/null +++ b/ArangoProvider.php @@ -0,0 +1,133 @@ +arango = Instance::ensure($this->arango, app\components\ArangoDbConnection::className()); + if ($this->collection === null) { + throw new InvalidConfigException('The "collection" property must be set.'); + } + } + + /** + * @inheritdoc + */ + protected function prepareKeys($models) + { + + return array_keys($models); + + } + + /** + * @inheritdoc + */ + protected function prepareModels() + { + // $sql = $this->sql; + // $qb = $this->db->getQueryBuilder(); + // if (($sort = $this->getSort()) !== false) { + // $orderBy = $qb->buildOrderBy($sort->getOrders()); + // if (!empty($orderBy)) { + // $orderBy = substr($orderBy, 9); + // if (preg_match('/\s+order\s+by\s+[\w\s,\.]+$/i', $sql)) { + // $sql .= ', ' . $orderBy; + // } else { + // $sql .= ' ORDER BY ' . $orderBy; + // } + // } + // } + + // if (($pagination = $this->getPagination()) !== false) { + // $pagination->totalCount = $this->getTotalCount(); + // $sql .= ' ' . $qb->buildLimit($pagination->getLimit(), $pagination->getOffset()); + // } + + // return $this->db->createCommand($sql, $this->params)->queryAll(); + $statement = $this->getBaseStatement(); + + if (($pagination = $this->getPagination()) !== false) { + $pagination->totalCount = $this->getTotalCount(); + $statement->setQuery($statement->getQuery() . "\n LIMIT " . $pagination->getOffset() . ", " . $pagination->getLimit()); + } + + + $statement->setQuery($statement->getQuery()."\n RETURN a"); + $cursor = $statement->execute(); + $data = $cursor->getAll(); + $result = []; + foreach ($data as $doc) { + $item = $doc->getAll(); + foreach ($item as $k=>$v) { + if (is_array($item[$k]) || is_object($item[$k])) { + $item[$k] = json_encode($v, true); + } + } + $result[$item['_key']] = $item; + } + $pagination->totalCount = $cursor->getFullCount(); + + return $result; + } + + public function getTotalCount() { + $statement = $this->getBaseStatement(); + $statement->setQuery($statement->getQuery(). "\n LIMIT 1 \n RETURN a"); + + + $cursor = $statement->execute(); + return $cursor->getFullCount(); + } + + private function getBaseStatement() { + $query = "FOR a in @@collection\n"; + + $filter = []; + $bindings = ['@collection' => $this->collection]; + $counter = 0; + foreach ($this->params as $k => $v) { + $filter[] = " a.@filter_field_$counter == @filter_value_$counter "; + $bindings["filter_field_$counter"] = $k; + $bindings["filter_value_$counter"] = $v; + $counter++; + } + if (count($filter)>0){ + $query .= "\nFILTER ".implode(" && ", $filter)."\n"; + } + $statement = $this->arango->statement([ + 'query' => $query, + 'count' => true, + 'bindVars' => $bindings, + 'fullCount' => true, + ]); + return $statement; + } + + /** + * @inheritdoc + */ + protected function prepareTotalCount() + { + return 0; + } +} \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..8feb93a --- /dev/null +++ b/composer.json @@ -0,0 +1,22 @@ +{ + "name": "bethrezen/yii2-arangodb", + "description": "Yii2 arangodb components", + "type": "yii2-extension", + "keywords": ["yii2","arangodb"], + "license": "GPL-3.0+", + "authors": [ + { + "name": "Alexander Kozhevnikov", + "email": "b37hr3z3n@gmail.com" + } + ], + "require": { + "yiisoft/yii2": "*", + "triagens/arangodb": "*" + }, + "autoload": { + "psr-4": { + "bethrezen\\arangodb\\": "" + } + } +} \ No newline at end of file