From 076f91f51b14b226c80a050ad58fd95a64647aa4 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Mon, 16 Jan 2017 15:57:58 +0000 Subject: [PATCH 01/53] trying HUGE redo --- src/ActiveQuery.php | 13 ++--- src/ActiveRecord.php | 38 +++++++------- src/Collection.php | 23 ++++----- src/Command.php | 115 +++++++++++++++++++++++++++++++------------ src/Connection.php | 50 ++++++++++++------- src/Query.php | 39 +++++++++++++-- src/QueryBuilder.php | 53 ++++++++++---------- 7 files changed, 209 insertions(+), 122 deletions(-) diff --git a/src/ActiveQuery.php b/src/ActiveQuery.php index a9fdbb3..0c4c657 100644 --- a/src/ActiveQuery.php +++ b/src/ActiveQuery.php @@ -249,11 +249,9 @@ public function addSelect($columns) /** * Executes query and returns all results as an array. - * * @param Connection $db the DB connection used to create the DB command. - * If null, the DB connection returned by [[modelClass]] will be used. - * - * @return array the query results. If the query results in nothing, an empty array will be returned. + * If null, the DB connection returned by [[modelClass]] will be used. + * @return array|ActiveRecord[] the query results. If the query results in nothing, an empty array will be returned. */ public function all($db = null) { @@ -411,11 +409,10 @@ private function addInverseRelation($relatedModel) * Executes query and returns a single row of result. * * @param Connection $db the DB connection used to create the DB command. - * If null, the DB connection returned by [[modelClass]] will be used. - * + * If null, the DB connection returned by [[modelClass]] will be used. * @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]], - * the query result may be either an array or an ActiveRecord object. Null will be returned - * if the query results in nothing. + * the query result may be either an array or an ActiveRecord object. Null will be returned + * if the query results in nothing. */ public function one($db = null) { diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index 0562afe..99d21dd 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -35,15 +35,16 @@ public static function getDb() /** * {@inheritdoc} - * * @return ActiveQuery the newly created [[ActiveQuery]] instance */ public static function find($options = []) { $config = [ 'class' => ActiveQuery::class, + 'db' => $this->getDb(), 'options' => $options, ]; + return Yii::createObject($config, [get_called_class()]); } @@ -262,11 +263,8 @@ public function insert($runValidation = true, $attributes = null, $options = []) } $values = $this->getDirtyAttributes($attributes); - - $command = $this->getScenarioCommand('create'); - $data = array_merge($values, $options, ['id' => $this->getOldPrimaryKey()]); - - $result = static::getDb()->createCommand()->perform($command, $data); + $data = array_merge($values, $options, ['id' => $this->getOldPrimaryKey()]); + $result = $this->perform('create', $data); $pk = static::primaryKey()[0]; $this->$pk = $result['id']; @@ -289,10 +287,8 @@ public function delete($options = []) return false; } - $command = $this->getScenarioCommand('delete'); - $data = array_merge($options, ['id' => $this->getOldPrimaryKey()]); - - $result = static::getDb()->createCommand()->perform($command, $data); + $data = array_merge($options, ['id' => $this->getOldPrimaryKey()]); + $result = $this->perform('delete', $data); $this->setOldAttributes(null); $this->afterDelete(); @@ -316,18 +312,15 @@ protected function updateInternal($attributes = null, $options = []) } $values = $this->getAttributes($attributes); -// $values = $this->attributes; - if (empty($values)) { $this->afterSave(false, $values); return 0; } - $command = $this->getScenarioCommand('update'); - $data = array_merge($values, $options, ['id' => $this->getOldPrimaryKey()]); + $data = array_merge($values, $options, ['id' => $this->getOldPrimaryKey()]); + $result = $this->perform('update', $data); - $result = static::getDb()->createCommand()->perform($command, $data); $changedAttributes = []; foreach ($values as $name => $value) { $changedAttributes[$name] = $this->getOldAttribute($name); @@ -339,16 +332,21 @@ protected function updateInternal($attributes = null, $options = []) return $result === false ? false : true; } + protected function perform($defaultScenario, $data, $bulk = false) + { + $command = $this->getScenarioCommand($defaultScenario, $bulk); + + return static::getDb()->createCommand()->perform($command, $data); + } + /** - * Custom method for HiArt. - * + * Creates and performs action statically. * @param $action * @param array $options * @param bool $bulk - * * @return array */ - public static function perform($action, $options = [], $bulk = false) + public static function performAction($action, $options = [], $bulk = false) { $action = ($bulk === true ? static::index() : static::type()) . $action; $result = static::getDb()->createCommand()->perform($action, $options); @@ -357,7 +355,7 @@ public static function perform($action, $options = [], $bulk = false) } /** - * Creates the command name for the specified scenario name. + * Creates command name from the current scenario name. * * @param string $default * @param bool $bulk diff --git a/src/Collection.php b/src/Collection.php index 4e77f9f..d28a0c1 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -341,9 +341,7 @@ public function insert($runValidation = true, $attributes = null, $options = []) } $data = $this->collectData($attributes, $options); - $command = $this->first->getScenarioCommand('create', true); - - $results = $this->first->getDb()->createCommand()->perform($command, $data); + $results = $this->first->perform('create', $data, true); $pk = $this->first->primaryKey()[0]; foreach ($this->models as $key => $model) { /* @var $model ActiveRecord */ @@ -377,8 +375,7 @@ public function update($runValidation = true, $attributes = null, $options = []) } $data = $this->collectData($attributes, $options); - $command = $this->first->getScenarioCommand('update', true); - $results = $this->first->getDb()->createCommand()->perform($command, $data); + $results = $this->first->perform('update', $data, true); foreach ($this->models as $key => $model) { $changedAttributes = []; @@ -398,16 +395,16 @@ public function update($runValidation = true, $attributes = null, $options = []) public function delete() { - $result = false; - if ($this->beforeDelete()) { - $data = $this->collectData(); - $command = $this->first->getScenarioCommand('delete', true); - - $results = $this->first->getDb()->createCommand()->perform($command, $data); - $this->afterDelete(); + if (!$this->beforeDelete()) { + return false; } - return $result; + $data = $this->collectData(); + $results = $this->first->perform('delete', $data, true); + + $this->afterDelete(); + + return $results; } /** diff --git a/src/Command.php b/src/Command.php index 74f8c44..fea761e 100644 --- a/src/Command.php +++ b/src/Command.php @@ -16,7 +16,7 @@ use yii\helpers\Json; /** - * The Command class implements the API for accessing REST API. + * The Command class implements execution of query. */ class Command extends Component { @@ -24,46 +24,73 @@ class Command extends Component * @var Connection */ public $db; + + /** + * @var Query Query object + */ + public $query; + /** - * @var string|array the indexes to execute the query on. Defaults to null meaning all indexes + * @var string request method e.g. POST */ - public $index; + protected $method; + /** - * @var string|array the types to execute the query on. Defaults to null meaning all types + * @var string request url, without site */ - public $type; + protected $url; + /** - * @var array list of arrays or json strings that become parts of a query + * @var array request query vars (GET parameters) */ - public $queryParts = []; + protected $queryVars; /** - * Sends a request to the _search API and returns the result. + * @var string request body vars (POST parameters) + */ + protected $body; + + /** + * @var bool do not decode request + */ + protected $raw = false; + + /** + * Sends a request to retrieve data. + * In API this could be get, search or list request. * @param array $options * @throws ErrorResponseException * @return mixed */ public function search($options = []) { - $url = $this->index . Inflector::id2camel(ArrayHelper::remove($options, 'scenario', 'search')); - $query = $this->queryParts; - $options = array_merge($query, $options); + return $this->makeRequest('search', $options); + } + + /** + * Sends a request to create/insert data. + * @param mixed $from entity to create + * @param mixed $data attributes of object to create + * @param mixed $options operation options + * @return mixed + */ + public function create($from, $data, array $options = []) + { + $this->query->from($from)->addParts($data); - return $this->db->post($url, $options); + return $this->makeRequest('create', $options); } - public function insert($action, $data, $id = null, $options = []) + public function update($index, $data, $where, $options = []) { - $options = array_merge($data, $options); + $options['id'] = $id; + + return $this->db->put($index . 'Update', array_merge($data, $options)); - if ($id !== null) { - return $this->db->put($action . 'Update', array_merge($options, ['id' => $id])); - } else { - return $this->db->post($action . 'Create', $options); - } + return $this->makeRequest('update', $options); } - public function get($modelName, $primaryKey, $options) + public function get($modelName, $primaryKey, $options = []) { return $this->db->post($modelName . 'GetInfo', ArrayHelper::merge(['id' => $primaryKey], $options)); } @@ -80,25 +107,51 @@ public function exists($index, $type, $id) return $this->db->head([$index, $type, $id]); } - public function delete($index, $id, $options = []) + public function delete($from, $id, $options = []) { - return $this->db->delete($index . 'Delete', array_merge($options, ['id' => $id])); - } + $this->query->from($from)->where(['id' => $id]); - public function update($index, $id, $data, $options = []) - { - $options['id'] = $id; - - return $this->db->put($index . 'Update', array_merge($data, $options)); + return $this->makeRequest('delete', $options); } /** - * @param $action + * Performs str + * @param string $url URL * @param mixed $body request parameters * @return mixed */ - public function perform($action, $body = []) + public function perform($url, $body = []) + { + return $this->db->post($url, [], $body); + return $this->makeRequest($action, $options); + } + + public function makeRequest($method, $action, array $options = []) + { + return $this->db->makeRequest( + $this->buildMethod($action, $options), + $this->buildUrl($action, $options), + $this->buildQuery($action, $options), + $this->buildBody($action, $options), + $this->buildRaw($action, $options) + ); + } + + public function buildUrl($action, array $options) + { + return $query->from . Inflector::id2camel($action); + } + + public function getQueryVars($action, $options) + { + } + + public function getBody($action, $options) + { + } + + public function getRaw($action, $options) { - return $this->db->post($action, [], $body); + return $this->raw; } } diff --git a/src/Connection.php b/src/Connection.php index 8c1313c..21d9987 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -43,9 +43,14 @@ class Connection extends Component public $config = []; /** - * @var Handler + * @var Handler request handler */ - protected static $_handler = null; + protected static $_handler; + + /** + * @var QueryBuilder the query builder for this connection + */ + protected $_builder; /** * @var array authorization config @@ -121,7 +126,6 @@ public function __sleep() /** * Returns the name of the DB driver for the current [[dsn]]. - * * @return string name of the DB driver */ public function getDriverName() @@ -131,25 +135,33 @@ public function getDriverName() /** * Creates a command for execution. - * * @param array $config the configuration for the Command class - * * @return Command the DB command */ public function createCommand($config = []) { $config['db'] = $this; - $command = new Command($config); - return $command; + return new Command($config); + } + + /** + * @return QueryBuilder the query builder for this connection. + */ + public function getQueryBuilder() + { + if ($this->_builder === null) { + $this->_builder = $this->createQueryBuilder(); + } + + return $this->_builder; } /** * Creates new query builder instance. - * * @return QueryBuilder */ - public function getQueryBuilder() + public function createQueryBuilder() { return new QueryBuilder($this); } @@ -157,7 +169,7 @@ public function getQueryBuilder() /** * Performs GET HTTP request. * @param string $url URL - * @param array $query query options + * @param array $query query options (GET parameters) * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` * @throws HiArtException * @throws \yii\base\InvalidConfigException @@ -185,8 +197,8 @@ public function head($url, $query = [], $body = null) /** * Performs POST HTTP request. * @param string $url URL - * @param array $query query options - * @param string $body request body + * @param array $query query options (GET parameters) + * @param string $body request body (POST parameters) * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` * @throws HiArtException * @throws \yii\base\InvalidConfigException @@ -200,8 +212,8 @@ public function post($url, $query = [], $body = null, $raw = false) /** * Performs PUT HTTP request. * @param string $url URL - * @param array $query query options - * @param string $body request body + * @param array $query query options (GET parameters) + * @param string $body request body (POST parameters) * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` * @throws HiArtException * @throws \yii\base\InvalidConfigException @@ -215,8 +227,8 @@ public function put($url, $query = [], $body = null, $raw = false) /** * Performs DELETE HTTP request. * @param string $url URL - * @param array $query query options - * @param string $body request body + * @param array $query query options (GET parameters) + * @param string $body request body (POST parameters) * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` * @throws HiArtException * @throws \yii\base\InvalidConfigException @@ -241,8 +253,8 @@ public function perform($url, $body = []) /** * Make request and check for error. * @param string $url URL - * @param array $query query options, (GET parameters) - * @param string $body request body, (POST parameters) + * @param array $query query options (GET parameters) + * @param string $body request body (POST parameters) * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` * @throws HiArtException * @throws \yii\base\InvalidConfigException @@ -256,7 +268,7 @@ public function makeRequest($method, $url, $query = [], $body = null, $raw = fal } /** - * Creates URL. + * Creates URL by joining path part and query options. * @param mixed $path path * @param array $query query options * @return array diff --git a/src/Query.php b/src/Query.php index 91fd95b..e5b48e0 100644 --- a/src/Query.php +++ b/src/Query.php @@ -15,14 +15,29 @@ use yii\db\QueryInterface; use yii\db\QueryTrait; +/** + * Query represents API request in a way that is independent from concrete API. + * Holds request data: + * - select: fields to select + * - from: entity being queried, e.g. user + * - join: data how to join with other entities + * - parts: [key => value] combined data of request to be passed as GET or POST variables + * - other standard request options provided with QueryTrait: limit, offset, orderBy, ... + */ class Query extends Component implements QueryInterface { use QueryTrait; - public $index; - public $type; + public $db; + public $select; + public $from; public $join; + public $parts; + + /// DEPRECATED + public $index; + public $type; /** * {@inheritdoc} @@ -212,10 +227,9 @@ public function filter($filter) return $this; } - public function from($index, $type = null) + public function from($from) { - $this->index = $index; - $this->type = $type; + $this->from = $from; return $this; } @@ -248,4 +262,19 @@ public function timeout($timeout) return $this; } + + public function getParts() + { + return $this->parts; + } + + public function setPart($name, $value) + { + $this->parts[$name] = $value; + } + + public function addParts($values) + { + $this->parts = ArrayHelper::merge($this->parts, $values); + } } diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 5ea2819..bc9ef49 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -33,64 +33,65 @@ public function __construct($connection, $config = []) } /** - * @param ActiveQuery $query - * + * @param Query $query * @throws NotSupportedException - * * @return array */ public function build($query) { - $parts = []; $query->prepare(); - $this->buildSelect($query->select, $parts); - $this->buildLimit($query->limit, $parts); - $this->buildPage($query->offset, $query->limit, $parts); - $this->buildOrderBy($query->orderBy, $parts); - - $parts = ArrayHelper::merge($parts, $this->buildCondition($query->where)); + $this->buildSelect($query); + $this->buildLimit($query); + $this->buildPage($query); + $this->buildOrderBy($query); + $this->buildWhere($query); - return [ - 'queryParts' => $parts, - 'index' => $query->index, - 'type' => $query->type, - ]; + return ['query' => $query]; } - public function buildLimit($limit, &$parts) + public function buildLimit(Query $query) { + $limit = $query->limit; if (!empty($limit)) { if ($limit === -1) { $limit = 'ALL'; } - $parts['limit'] = $limit; + $query->setPart('limit', $limit); } } - public function buildPage($offset, $limit, &$parts) + public function buildPage(Query $query) { - if ($offset > 0) { - $parts['page'] = ceil($offset / $limit) + 1; + if ($query->offset > 0) { + $this->setPart('page', ceil($this->offset / $this->limit) + 1); } } - public function buildOrderBy($orderBy, &$parts) + public function buildOrderBy(Query $query) { + $orderBy = $query->orderBy; if (!empty($orderBy)) { - $parts['orderby'] = key($orderBy) . $this->_sort[reset($orderBy)]; + $this->setPart('orderby', key($orderBy) . $this->_sort[reset($orderBy)]); } } - public function buildSelect($select, &$parts) + public function buildSelect(Query $query) { - if (!empty($select)) { - foreach ($select as $attribute) { - $parts['select'][$attribute] = $attribute; + if (!empty($query->select)) { + $select = []; + foreach ($query->select as $name) { + $select[$name] = $name; } + $this->setPart('select', $select); } } + public function buildWhere(Query $query) + { + $query->addParts($this->buildCondition($query->where)); + } + public function buildCondition($condition) { static $builders = [ From 95bd315eb8169a18a86c0720e41210ae0362283c Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 17 Jan 2017 14:31:56 +0000 Subject: [PATCH 02/53] still trying HUGE redo NOT FINISHED --- src/ActiveQuery.php | 21 ++----- src/ActiveRecord.php | 20 ++----- src/Command.php | 133 +++++++++++++------------------------------ src/Connection.php | 25 ++++---- src/Query.php | 57 ++++++++++++++++--- src/QueryBuilder.php | 106 ++++++++++++++++++++++------------ 6 files changed, 184 insertions(+), 178 deletions(-) diff --git a/src/ActiveQuery.php b/src/ActiveQuery.php index 0c4c657..1072957 100644 --- a/src/ActiveQuery.php +++ b/src/ActiveQuery.php @@ -33,14 +33,8 @@ class ActiveQuery extends Query implements ActiveQueryInterface */ public $joinWith = []; - /** - * @var array options for search - */ - public $options = []; - /** * Constructor. - * * @param string $modelClass the model class associated with this query * @param array $config configurations to be applied to the newly created query object */ @@ -65,10 +59,8 @@ public function init() /** * Creates a DB command that can be used to execute this query. - * * @param Connection $db the DB connection used to create the DB command. - * If null, the DB connection returned by [[modelClass]] will be used. - * + * If null, the DB connection returned by [[modelClass]] will be used. * @return Command the created DB command instance */ public function createCommand($db = null) @@ -95,16 +87,12 @@ public function createCommand($db = null) /* @var $modelClass ActiveRecord */ $modelClass = $this->modelClass; + if ($db === null) { $db = $modelClass::getDb(); } - - if ($this->type === null) { - $this->type = $modelClass::type(); - } - if ($this->index === null) { - $this->index = $modelClass::index(); - $this->type = $modelClass::type(); + if ($this->from === null) { + $this->from = $modelClass::from(); } $commandConfig = $db->getQueryBuilder()->build($this); @@ -113,6 +101,7 @@ public function createCommand($db = null) } /** + * Prepares query for use. See NOTE. * @return static */ public function prepare() diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index 99d21dd..d1448ca 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -41,7 +41,6 @@ public static function find($options = []) { $config = [ 'class' => ActiveQuery::class, - 'db' => $this->getDb(), 'options' => $options, ]; @@ -88,7 +87,7 @@ public static function get($primaryKey = null, $options = []) return null; } $command = static::getDb()->createCommand(); - $result = $command->get(static::type(), $primaryKey, $options); + $result = $command->get(static::from(), $primaryKey, $options); if ($result) { $model = static::instantiate($result); @@ -220,12 +219,6 @@ public static function joinIndex() * For example, by creating a record based on the value of a column, * you may implement the so-called single-table inheritance mapping. * - * @param array $row row data to be populated into the record. - * This array consists of the following keys: - * - `_source`: refers to the attributes of the record. - * - `_type`: the type this record is stored in. - * - `_index`: the index this record is stored in. - * * @return static the newly created active record */ public static function instantiate($row) @@ -234,9 +227,9 @@ public static function instantiate($row) } /** - * @return string the name of the type of this record + * @return string the name of the entity of this record */ - public static function type() + public static function from() { return Inflector::camel2id(StringHelper::basename(get_called_class()), '-'); } @@ -348,7 +341,7 @@ protected function perform($defaultScenario, $data, $bulk = false) */ public static function performAction($action, $options = [], $bulk = false) { - $action = ($bulk === true ? static::index() : static::type()) . $action; + $action = ($bulk === true ? static::index() : static::from()) . $action; $result = static::getDb()->createCommand()->perform($action, $options); return $result; @@ -356,13 +349,10 @@ public static function performAction($action, $options = [], $bulk = false) /** * Creates command name from the current scenario name. - * * @param string $default * @param bool $bulk - * * @throws InvalidConfigException * @throws NotSupportedException - * * @return string */ public function getScenarioCommand($default = '', $bulk = false) @@ -395,7 +385,7 @@ public function getScenarioCommand($default = '', $bulk = false) if (is_array($result)) { return implode('', $result); } else { - return static::type() . ($bulk ? 's' : '') . $result; + return ($bulk ? 's' : '') . $result; } } diff --git a/src/Command.php b/src/Command.php index fea761e..471962b 100644 --- a/src/Command.php +++ b/src/Command.php @@ -10,15 +10,12 @@ namespace hiqdev\hiart; -use yii\base\Component; -use yii\helpers\ArrayHelper; -use yii\helpers\Inflector; -use yii\helpers\Json; +use Psr\Http\Message\RequestInterface; /** - * The Command class implements execution of query. + * The Command class implements execution of request. */ -class Command extends Component +class Command extends \yii\base\Component { /** * @var Connection @@ -26,132 +23,80 @@ class Command extends Component public $db; /** - * @var Query Query object + * @var RequestInterface request object */ - public $query; + protected $_request; - /** - * @var string request method e.g. POST - */ - protected $method; - - /** - * @var string request url, without site - */ - protected $url; - - /** - * @var array request query vars (GET parameters) - */ - protected $queryVars; - - /** - * @var string request body vars (POST parameters) - */ - protected $body; + public function setRequest(RequestInterface $request) + { + $this->_request = $request; - /** - * @var bool do not decode request - */ - protected $raw = false; + return $this; + } /** + * XXX IN QUESTION * Sends a request to retrieve data. * In API this could be get, search or list request. - * @param array $options * @throws ErrorResponseException * @return mixed */ - public function search($options = []) + public function search() { - return $this->makeRequest('search', $options); + return $this->execute(); } /** * Sends a request to create/insert data. - * @param mixed $from entity to create - * @param mixed $data attributes of object to create - * @param mixed $options operation options + * @param mixed $table entity to create + * @param mixed $columns attributes of object to create * @return mixed */ - public function create($from, $data, array $options = []) + public function insert($table, $columns, array $options = []) { - $this->query->from($from)->addParts($data); + $request = $this->db->getQueryBuilder()->insert($table, $columns, $options); - return $this->makeRequest('create', $options); + return $this->setRequest($request); } - public function update($index, $data, $where, $options = []) + public function update($table, $columns, $condition = [], array $options = []) { - $options['id'] = $id; - - return $this->db->put($index . 'Update', array_merge($data, $options)); + $request = $this->db->getQueryBuilder()->update($table, $columns, $condition, $options); - return $this->makeRequest('update', $options); + return $this->setRequest($request); } - public function get($modelName, $primaryKey, $options = []) + public function delete($table, $condition, array $options = []) { - return $this->db->post($modelName . 'GetInfo', ArrayHelper::merge(['id' => $primaryKey], $options)); - } - - public function mget($index, $type, $ids, $options = []) - { - $body = Json::encode(['ids' => array_values($ids)]); + $request = $this->db->getQueryBuilder()->delete($table, $condition, $options); - return $this->db->post([$index, $type, '_mget'], $options, $body); - } - - public function exists($index, $type, $id) - { - return $this->db->head([$index, $type, $id]); - } - - public function delete($from, $id, $options = []) - { - $this->query->from($from)->where(['id' => $id]); - - return $this->makeRequest('delete', $options); + return $this->setRequest($request); } /** - * Performs str + * Executes the request. * @param string $url URL * @param mixed $body request parameters * @return mixed */ - public function perform($url, $body = []) - { - return $this->db->post($url, [], $body); - return $this->makeRequest($action, $options); - } - - public function makeRequest($method, $action, array $options = []) - { - return $this->db->makeRequest( - $this->buildMethod($action, $options), - $this->buildUrl($action, $options), - $this->buildQuery($action, $options), - $this->buildBody($action, $options), - $this->buildRaw($action, $options) - ); - } - - public function buildUrl($action, array $options) + public function execute() { - return $query->from . Inflector::id2camel($action); + var_dump($this->_request); + die(); + return $this->db->send($this->_request); } - public function getQueryVars($action, $options) - { - } - - public function getBody($action, $options) + /** + * Creates and executes request with given data. + * @param string $action + * @param mixed $body request parameters + * @return mixed + */ + public function perform($action, $body = []) { - } + $request = $this->db->getQueryBuilder()->perform($action, $body); + $this->setRequest($request); - public function getRaw($action, $options) - { - return $this->raw; + return $this->execute(); } } diff --git a/src/Connection.php b/src/Connection.php index 21d9987..9cfa639 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -12,6 +12,8 @@ use Closure; use GuzzleHttp\Client as Handler; +use Psr\Http\Message\RequestInterface; +use Psr\Http\Message\ResponseInterface; use Yii; use yii\base\Component; use yii\base\InvalidConfigException; @@ -239,16 +241,6 @@ public function delete($url, $query = [], $body = null, $raw = false) return $this->makeRequest('DELETE', $url, $query, $body, $raw); } - /** - * XXX DEPRECATED in favour of post(). - * @param $url - * @param array $query - * @return mixed - */ - public function perform($url, $body = []) - { - return $this->makeRequest('DELETE', $url, [], $body); - } /** * Make request and check for error. @@ -329,11 +321,22 @@ public function getHandler() * Set handler manually. * @param Handler $value */ - public function setHandler($value) + public function setHandler(Handler $value) { static::$_handler = $value; } + /** + * Sends given request. + * @param RequestInterface $request + * @param array $options + * @return ResponseInterface + */ + public function send(RequestInterface $request, array $options = []) + { + return $this->getHandler()->send($request, $options); + } + /** * @return boolean */ diff --git a/src/Query.php b/src/Query.php index e5b48e0..5046a78 100644 --- a/src/Query.php +++ b/src/Query.php @@ -14,15 +14,22 @@ use yii\base\Component; use yii\db\QueryInterface; use yii\db\QueryTrait; +use yii\helpers\ArrayHelper; /** * Query represents API request in a way that is independent from concrete API. - * Holds request data: - * - select: fields to select - * - from: entity being queried, e.g. user - * - join: data how to join with other entities - * - parts: [key => value] combined data of request to be passed as GET or POST variables - * - other standard request options provided with QueryTrait: limit, offset, orderBy, ... + * Holds API request information: + * - data passed into query: + * - action: action to be performed with this query, e.g. search, insert, update, delete + * - options: other additional options + * - select: fields to select + * - from: entity being queried, e.g. user + * - join: data how to join with other entities + * - other standard request options provided with QueryTrait: limit, offset, orderBy, ... + * - data build with QueryBuilder: + * - HTTP request data: method, url, raw + * - in question: queryVars, body ???? + * - parts: [key => value] combined data of request to be passed as GET or POST variables */ class Query extends Component implements QueryInterface { @@ -30,11 +37,46 @@ class Query extends Component implements QueryInterface public $db; + /** + * @var string action that this query performs + */ + public $action; + + /** + * @var array options for search + */ + public $options = []; + public $select; public $from; public $join; public $parts; + /** + * @var string request method e.g. POST + */ + public $method; + + /** + * @var string request url, without site + */ + public $url; + + /** + * @var array request query vars (GET parameters) + */ + public $queryVars; + + /** + * @var string request body vars (POST parameters) + */ + public $body; + + /** + * @var bool do not decode request + */ + public $raw = false; + /// DEPRECATED public $index; public $type; @@ -51,9 +93,10 @@ public function init() } } - public function createCommand($db = null) + public function createCommand($db) { if ($db === null) { + throw new \Exception('no db given to Query::createCommand'); $db = Yii::$app->get('hiart'); } diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index bc9ef49..a863454 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -10,12 +10,14 @@ namespace hiqdev\hiart; +use GuzzleHttp\Psr7\ServerRequest; + use yii\base\InvalidParamException; use yii\base\NotSupportedException; use yii\helpers\ArrayHelper; /** - * QueryBuilder builds an HiArt query based on the specification given as a [[Query]] object. + * QueryBuilder builds a PSR7 request based on the specification given as a [[Query]] object. */ class QueryBuilder extends \yii\base\Object { @@ -37,59 +39,93 @@ public function __construct($connection, $config = []) * @throws NotSupportedException * @return array */ - public function build($query) + public function build(Query $query) { - $query->prepare(); + var_dump($query);die(); + $this->prepare($query); - $this->buildSelect($query); - $this->buildLimit($query); - $this->buildPage($query); - $this->buildOrderBy($query); - $this->buildWhere($query); + return ['request' => $this->buildRequest($query)]; + } - return ['query' => $query]; + /** + * Prepares query. This is function for you to redefine. + * @param Query $query + */ + public function prepare(Query $query) + { + $query->prepare(); } - public function buildLimit(Query $query) + public function buildRequest($query) { - $limit = $query->limit; - if (!empty($limit)) { - if ($limit === -1) { - $limit = 'ALL'; + $request = new ServerRequest($this->buildMethod($query), $this->buildUri($query)); + + $headers = $this->buildHeaders($query); + if (!empty($headers)) { + foreach ($headers as $header => $value) { + $request = $request->withHeader($header, $value); } - $query->setPart('limit', $limit); } + + foreach (['ProtocolVersion', 'UploadedFiles', 'CookieParams', 'QueryParams', 'ParsedBody'] as $name) { + $value = $this->{'build' . $name}($query); + if (!empty($value)) { + $request = $request->{'with' . $name}($value); + } + } + + return $request; } - public function buildPage(Query $query) + public function buildMethod(Query $query) { - if ($query->offset > 0) { - $this->setPart('page', ceil($this->offset / $this->limit) + 1); - } + static $defaultMethods = [ + 'get' => 'GET', + 'put' => 'PUT', + 'head' => 'HEAD', + 'post' => 'GET', + 'search' => 'GET', + 'insert' => 'POST', + 'update' => 'PUT', + 'delete' => 'DELETE', + ]; + + return isset($defaultMethods[$query->action]) ? $defaultMethods[$query->action] : 'POST'; } - public function buildOrderBy(Query $query) + public function buildUri(Query $query) { - $orderBy = $query->orderBy; - if (!empty($orderBy)) { - $this->setPart('orderby', key($orderBy) . $this->_sort[reset($orderBy)]); - } + return $query->from; } - public function buildSelect(Query $query) + public function buildHeaders(Query $query) { - if (!empty($query->select)) { - $select = []; - foreach ($query->select as $name) { - $select[$name] = $name; - } - $this->setPart('select', $select); - } + return []; + } + + public function buildProtocolVersion(Query $query) + { + return null; + } + + public function buildUploadedFiles(Query $query) + { + return []; + } + + public function buildCookieParams(Query $query) + { + return []; + } + + public function buildQueryParams(Query $query) + { + return []; } - public function buildWhere(Query $query) + public function buildParsedBody(Query $query) { - $query->addParts($this->buildCondition($query->where)); + return []; } public function buildCondition($condition) @@ -169,7 +205,7 @@ private function buildAndCondition($operator, $operands) $parts = []; foreach ($operands as $operand) { if (is_array($operand)) { - $parts = \yii\helpers\ArrayHelper::merge($this->buildCondition($operand), $parts); + $parts = ArrayHelper::merge($this->buildCondition($operand), $parts); } } if (!empty($parts)) { From ce8fb9deb97f368c4cd580a9adc5c3ddb0790fae Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Wed, 18 Jan 2017 13:24:50 +0000 Subject: [PATCH 03/53] added Request and Response wrappers --- src/Request.php | 71 +++++++++++++++++++++++++++++++++++++++++ src/Response.php | 83 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 src/Request.php create mode 100644 src/Response.php diff --git a/src/Request.php b/src/Request.php new file mode 100644 index 0000000..1b0918d --- /dev/null +++ b/src/Request.php @@ -0,0 +1,71 @@ +worker = $worker; + $this->query = $query; + } + + public function getWorker() + { + return $this->worker; + } + + public static function fromData(array $data) + { + $request = new ServerRequest($data['Method'], $data['Uri']); + + if (!empty($data['Headers'])) { + foreach ($data['Headers'] as $header => $value) { + $request = $request->withHeader($header, $value); + } + } + + foreach (['ProtocolVersion', 'UploadedFiles', 'CookieParams', 'QueryParams', 'ParsedBody'] as $name) { + $value = $data[$name]; + if (!empty($value)) { + $request = $request->{'with' . $name}($value); + } + } + + return new static($request, $data['query']); + } + + public function getProfile() + { + /// TODO serialize request object for profile + $request = $this->worker; + $body = array_merge($request->getParsedBody(), $request->getQueryParams()); + + return $request->getMethod() . ' ' . $request->getUri() . '#' . http_build_query($body); + } + + public function isRaw() + { + return !empty($this->query->options['raw']); + } +} diff --git a/src/Response.php b/src/Response.php new file mode 100644 index 0000000..1bd5052 --- /dev/null +++ b/src/Response.php @@ -0,0 +1,83 @@ +worker = $worker; + $this->request = $request; + $this->init(); + } + + public function getWorker() + { + return $this->worker; + } + + public function getRequest() + { + return $this->request; + } + + public function getData() + { + return $this->data; + } + + public function init() + { + $this->data = $this->getBodyContents(); + if (!$this->isRaw() && $this->isJson()) { + $this->data = Json::decode($this->data); + } + } + + public function isRaw() + { + return $this->request->isRaw(); + } + + public function isJson() + { + return preg_grep('|application/json|i', $this->getHeader('Content-Type')); + } + + public function getHeader($name) + { + return $this->worker->getHeader($name); + } + + public function getBodyContents() + { + return $this->worker->getBody()->getContents(); + } + +} From 47a9ef5a3e33ed46a3373afbffe6d360534394fe Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Wed, 18 Jan 2017 13:25:23 +0000 Subject: [PATCH 04/53] more HUGE redoing with Request and Response NOT FINISHED --- src/ActiveQuery.php | 4 +- src/ActiveRecord.php | 66 +++------------ src/Collection.php | 6 +- src/Command.php | 43 ++++++---- src/Connection.php | 16 ++-- src/Query.php | 193 +++++++------------------------------------ src/QueryBuilder.php | 93 +++++++++++++-------- 7 files changed, 138 insertions(+), 283 deletions(-) diff --git a/src/ActiveQuery.php b/src/ActiveQuery.php index 1072957..238ef24 100644 --- a/src/ActiveQuery.php +++ b/src/ActiveQuery.php @@ -141,9 +141,9 @@ private function buildJoinWith() foreach ($with as $name => $callback) { if (is_int($name)) { - $this->join([$callback]); + $this->innerJoin([$callback]); } else { - $this->join([$name => $callback]); + $this->innerJoin([$name => $callback]); } unset($with[$name]); diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index d1448ca..ecf31af 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -37,35 +37,11 @@ public static function getDb() * {@inheritdoc} * @return ActiveQuery the newly created [[ActiveQuery]] instance */ - public static function find($options = []) + public static function find() { - $config = [ - 'class' => ActiveQuery::class, - 'options' => $options, - ]; + $class = static::getDb()->activeQueryClass; - return Yii::createObject($config, [get_called_class()]); - } - - /** - * {@inheritdoc} - */ - public static function findOne($condition, $options = []) - { - $query = static::find($options); - if (is_array($condition)) { - return $query->andWhere($condition)->one(); - } else { - return static::get($condition); - } - } - - /** - * {@inheritdoc} - */ - public static function findAll($condition, $options = []) - { - return static::find($options)->andWhere($condition)->all(); + return new $class(get_called_class()); } public function isScenarioDefault() @@ -257,7 +233,7 @@ public function insert($runValidation = true, $attributes = null, $options = []) $values = $this->getDirtyAttributes($attributes); $data = array_merge($values, $options, ['id' => $this->getOldPrimaryKey()]); - $result = $this->perform('create', $data); + $result = $this->perform('insert', $data); $pk = static::primaryKey()[0]; $this->$pk = $result['id']; @@ -325,37 +301,21 @@ protected function updateInternal($attributes = null, $options = []) return $result === false ? false : true; } - protected function perform($defaultScenario, $data, $bulk = false) + protected function perform($defaultScenario, $data, $batch = false) { - $command = $this->getScenarioCommand($defaultScenario, $bulk); + $command = $this->getScenarioCommand($defaultScenario); - return static::getDb()->createCommand()->perform($command, $data); - } - - /** - * Creates and performs action statically. - * @param $action - * @param array $options - * @param bool $bulk - * @return array - */ - public static function performAction($action, $options = [], $bulk = false) - { - $action = ($bulk === true ? static::index() : static::from()) . $action; - $result = static::getDb()->createCommand()->perform($action, $options); - - return $result; + return static::getDb()->createCommand()->perform($command, static::from(), $data, compact('batch')); } /** * Creates command name from the current scenario name. - * @param string $default - * @param bool $bulk + * @param string $default default command name * @throws InvalidConfigException * @throws NotSupportedException * @return string */ - public function getScenarioCommand($default = '', $bulk = false) + public function getScenarioCommand($default = '') { if ($this->isScenarioDefault()) { if ($default !== '') { @@ -364,7 +324,7 @@ public function getScenarioCommand($default = '', $bulk = false) throw new InvalidConfigException('Scenario not specified'); } } else { - $scenarioCommands = static::scenarioCommands($bulk); + $scenarioCommands = static::scenarioCommands(); if ($command = $scenarioCommands[$this->scenario]) { if ($command === false) { throw new NotSupportedException('The scenario can not be saved'); @@ -382,11 +342,7 @@ public function getScenarioCommand($default = '', $bulk = false) } } - if (is_array($result)) { - return implode('', $result); - } else { - return ($bulk ? 's' : '') . $result; - } + return is_array($result) ? implode('', $result) : $result; } /** diff --git a/src/Collection.php b/src/Collection.php index d28a0c1..9df1b16 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -230,15 +230,15 @@ public function load($data = null) if (isset($data[$this->formName])) { $data = $data[$this->formName]; - $is_bulk = true; + $is_batch = true; foreach ($data as $k => $v) { if (!is_array($v)) { - $is_bulk = false; + $is_batch = false; break; } } - if (!$is_bulk) { + if (!$is_batch) { $data = [$data]; } } elseif ($data['selection']) { diff --git a/src/Command.php b/src/Command.php index 471962b..ac0372d 100644 --- a/src/Command.php +++ b/src/Command.php @@ -10,7 +10,7 @@ namespace hiqdev\hiart; -use Psr\Http\Message\RequestInterface; +use Yii; /** * The Command class implements execution of request. @@ -23,13 +23,13 @@ class Command extends \yii\base\Component public $db; /** - * @var RequestInterface request object + * @var Request request object */ - protected $_request; + protected $request; - public function setRequest(RequestInterface $request) + public function setRequest(Request $request) { - $this->_request = $request; + $this->request = $request; return $this; } @@ -74,29 +74,36 @@ public function delete($table, $condition, array $options = []) } /** - * Executes the request. - * @param string $url URL + * Creates and executes request with given data. + * @param string $action * @param mixed $body request parameters * @return mixed */ - public function execute() + public function perform($action, $table, $body = [], array $options = []) { - var_dump($this->_request); - die(); - return $this->db->send($this->_request); + $request = $this->db->getQueryBuilder()->perform($action, $table, $body, $options); + $this->setRequest($request); + + return $this->execute(); } /** - * Creates and executes request with given data. - * @param string $action + * Executes the request. + * @param string $url URL * @param mixed $body request parameters * @return mixed */ - public function perform($action, $body = []) + public function execute() { - $request = $this->db->getQueryBuilder()->perform($action, $body); - $this->setRequest($request); - - return $this->execute(); + $profile = $this->request->getProfile(); + Yii::beginProfile($profile, __METHOD__); + $response = $this->db->send($this->request); + Yii::endProfile($profile, __METHOD__); + var_dump($response->getRequest()); + var_dump($response->getData()); + die(); + + return $res; } + } diff --git a/src/Connection.php b/src/Connection.php index 9cfa639..ce7b5d4 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -12,8 +12,6 @@ use Closure; use GuzzleHttp\Client as Handler; -use Psr\Http\Message\RequestInterface; -use Psr\Http\Message\ResponseInterface; use Yii; use yii\base\Component; use yii\base\InvalidConfigException; @@ -39,6 +37,10 @@ class Connection extends Component { const EVENT_AFTER_OPEN = 'afterOpen'; + public $queryClass = Query::class; + + public $activeQueryClass = ActiveQuery::class; + /** * @var array Config */ @@ -328,13 +330,15 @@ public function setHandler(Handler $value) /** * Sends given request. - * @param RequestInterface $request + * @param Request $request * @param array $options - * @return ResponseInterface + * @return Response */ - public function send(RequestInterface $request, array $options = []) + public function send(Request $request, array $options = []) { - return $this->getHandler()->send($request, $options); + $worker = $this->getHandler()->send($request->getWorker(), $options); + + return new Response($worker, $request); } /** diff --git a/src/Query.php b/src/Query.php index 5046a78..b3dbd97 100644 --- a/src/Query.php +++ b/src/Query.php @@ -13,91 +13,51 @@ use Yii; use yii\base\Component; use yii\db\QueryInterface; -use yii\db\QueryTrait; use yii\helpers\ArrayHelper; /** - * Query represents API request in a way that is independent from concrete API. - * Holds API request information: - * - data passed into query: + * Query represents API query in a way that is independent from a concrete API. + * Holds API query information: + * - general query data * - action: action to be performed with this query, e.g. search, insert, update, delete - * - options: other additional options + * - options: other additional options, like + * - raw: do not decode response + * - batch: batch(bulk) request + * - timeout, ... + * - insert/update query data + * - body: insert or update data + * - select query data * - select: fields to select * - from: entity being queried, e.g. user * - join: data how to join with other entities - * - other standard request options provided with QueryTrait: limit, offset, orderBy, ... - * - data build with QueryBuilder: - * - HTTP request data: method, url, raw - * - in question: queryVars, body ???? - * - parts: [key => value] combined data of request to be passed as GET or POST variables + * - other standard query options provided with QueryTrait: + * - where, limit, offset, orderBy, indexBy */ -class Query extends Component implements QueryInterface +class Query extends \yii\db\Query implements QueryInterface { - use QueryTrait; - - public $db; - /** * @var string action that this query performs */ public $action; /** - * @var array options for search + * @var array query options e.g. raw, batch */ public $options = []; - public $select; - public $from; - public $join; - public $parts; - - /** - * @var string request method e.g. POST - */ - public $method; - - /** - * @var string request url, without site - */ - public $url; - - /** - * @var array request query vars (GET parameters) - */ - public $queryVars; - - /** - * @var string request body vars (POST parameters) - */ - public $body; - - /** - * @var bool do not decode request - */ - public $raw = false; - - /// DEPRECATED - public $index; - public $type; + public $body = []; - /** - * {@inheritdoc} - */ - public function init() + public static function instantiate($action, $from, array $options = []) { - parent::init(); - // setting the default limit according to api defaults - if ($this->limit === null) { - $this->limit = 'ALL'; - } + $query = new static; + + return $query->action($action)->from($from)->options($options); } - public function createCommand($db) + public function createCommand($db = null) { if ($db === null) { throw new \Exception('no db given to Query::createCommand'); - $db = Yii::$app->get('hiart'); } $commandConfig = $db->getQueryBuilder()->build($this); @@ -105,38 +65,6 @@ public function createCommand($db) return $db->createCommand($commandConfig); } - public function join($type) - { - $this->join[] = (array) $type; - - return $this; - } - - public function all($db = null) - { - $result = $this->createCommand($db)->search(); - if (empty($result['hits']['hits'])) { - return []; - } - $rows = $result['hits']['hits']; - if ($this->indexBy === null) { - return $rows; - } - $models = []; - foreach ($rows as $key => $row) { - if ($this->indexBy !== null) { - if (is_string($this->indexBy)) { - $key = isset($row['fields'][$this->indexBy]) ? reset($row['fields'][$this->indexBy]) : $row['_source'][$this->indexBy]; - } else { - $key = call_user_func($this->indexBy, $row); - } - } - $models[$key] = $row; - } - - return $models; - } - public function one($db = null) { $result = $this->createCommand($db)->search(['limit' => 1]); @@ -172,44 +100,6 @@ public function delete($db = null, $options = []) return $this->createCommand($db)->deleteByQuery($options); } - public function scalar($field, $db = null) - { - $record = self::one($db); - if ($record !== false) { - if ($field === '_id') { - return $record['_id']; - } elseif (isset($record['_source'][$field])) { - return $record['_source'][$field]; - } elseif (isset($record['fields'][$field])) { - return count($record['fields'][$field]) === 1 ? reset($record['fields'][$field]) : $record['fields'][$field]; - } - } - - return null; - } - - public function column($field, $db = null) - { - $command = $this->createCommand($db); - $command->queryParts['_source'] = [$field]; - $result = $command->search(); - if (empty($result['hits']['hits'])) { - return []; - } - $column = []; - foreach ($result['hits']['hits'] as $row) { - if (isset($row['fields'][$field])) { - $column[] = $row['fields'][$field]; - } elseif (isset($row['_source'][$field])) { - $column[] = $row['_source'][$field]; - } else { - $column[] = null; - } - } - - return $column; - } - public function count($q = '*', $db = null) { $options = []; @@ -244,35 +134,30 @@ public function addAggregation($name, $type, $options) return $this; } - public function addAgg($name, $type, $options) + public function action($action) { - return $this->addAggregation($name, $type, $options); - } - - public function addSuggester($name, $definition) - { - $this->suggest[$name] = $definition; + $this->action = $action; return $this; } - public function query($query) + public function options($options) { - $this->query = $query; + $this->options = $options; return $this; } - public function filter($filter) + public function body($body) { - $this->filter = $filter; + $this->body = $body; return $this; } - public function from($from) + public function innerJoin($table, $on = '', $params = []) { - $this->from = $from; + $this->join[] = (array) $table; return $this; } @@ -298,26 +183,4 @@ public function source($source) return $this; } - - public function timeout($timeout) - { - $this->timeout = $timeout; - - return $this; - } - - public function getParts() - { - return $this->parts; - } - - public function setPart($name, $value) - { - $this->parts[$name] = $value; - } - - public function addParts($values) - { - $this->parts = ArrayHelper::merge($this->parts, $values); - } } diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index a863454..9a77ba6 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -10,22 +10,15 @@ namespace hiqdev\hiart; -use GuzzleHttp\Psr7\ServerRequest; - use yii\base\InvalidParamException; use yii\base\NotSupportedException; use yii\helpers\ArrayHelper; /** - * QueryBuilder builds a PSR7 request based on the specification given as a [[Query]] object. + * QueryBuilder builds a PSR7 request from the specification given as a [[Query]] object. */ class QueryBuilder extends \yii\base\Object { - private $_sort = [ - SORT_ASC => '_asc', - SORT_DESC => '_desc', - ]; - public $db; public function __construct($connection, $config = []) @@ -41,40 +34,39 @@ public function __construct($connection, $config = []) */ public function build(Query $query) { - var_dump($query);die(); - $this->prepare($query); - return ['request' => $this->buildRequest($query)]; } /** * Prepares query. This is function for you to redefine. - * @param Query $query + * @param Query $query */ public function prepare(Query $query) { - $query->prepare(); + $query->prepare($this); } public function buildRequest($query) { - $request = new ServerRequest($this->buildMethod($query), $this->buildUri($query)); - - $headers = $this->buildHeaders($query); - if (!empty($headers)) { - foreach ($headers as $header => $value) { - $request = $request->withHeader($header, $value); - } - } + $this->prepare($query); - foreach (['ProtocolVersion', 'UploadedFiles', 'CookieParams', 'QueryParams', 'ParsedBody'] as $name) { + $data = ['query' => $query]; + foreach (['Auth', 'Method', 'Uri', 'Headers', 'ProtocolVersion', 'UploadedFiles', 'CookieParams', 'QueryParams', 'ParsedBody'] as $name) { $value = $this->{'build' . $name}($query); if (!empty($value)) { - $request = $request->{'with' . $name}($value); + $data[$name] = $value; } } - return $request; + return Request::fromData($data); + } + + /** + * This function is for you to provide your authentication. + * @param Query $query + */ + public function buildAuth(Query $query) + { } public function buildMethod(Query $query) @@ -128,6 +120,39 @@ public function buildParsedBody(Query $query) return []; } + public function insert($table, $columns, array $options = []) + { + return $this->perform('insert', $table, $columns, $options); + } + + public function update($table, $columns, $condition = [], array $options = []) + { + $query = $this->createQuery('update', $table, $options)->body($columns)->where($condition); + + return $this->buildRequest($query); + } + + public function delete($table, $condition = [], array $options = []) + { + $query = $this->createQuery('delete', $table, $options)->where($condition); + + return $this->buildRequest($query); + } + + public function perform($action, $table, $body, $options = []) + { + $query = $this->createQuery($action, $table, $options)->body($body); + + return $this->buildRequest($query); + } + + public function createQuery($action, $table, array $options = []) + { + $class = $this->db->queryClass; + + return $class::instantiate($action, $table, $options); + } + public function buildCondition($condition) { static $builders = [ @@ -166,7 +191,7 @@ public function buildCondition($condition) } } - private function buildHashCondition($condition) + protected function buildHashCondition($condition) { $parts = []; foreach ($condition as $attribute => $value) { @@ -181,17 +206,17 @@ private function buildHashCondition($condition) return $parts; } - private function buildLikeCondition($operator, $operands) + protected function buildLikeCondition($operator, $operands) { return [$operands[0] . '_like' => $operands[1]]; } - private function buildIlikeCondition($operator, $operands) + protected function buildIlikeCondition($operator, $operands) { return [$operands[0] . '_ilike' => $operands[1]]; } - private function buildCompareCondition($operator, $operands) + protected function buildCompareCondition($operator, $operands) { if (!isset($operands[0], $operands[1])) { throw new InvalidParamException("Operator '$operator' requires three operands."); @@ -200,7 +225,7 @@ private function buildCompareCondition($operator, $operands) return [$operands[0] . '_' . $operator => $operands[1]]; } - private function buildAndCondition($operator, $operands) + protected function buildAndCondition($operator, $operands) { $parts = []; foreach ($operands as $operand) { @@ -215,12 +240,12 @@ private function buildAndCondition($operator, $operands) } } - private function buildBetweenCondition($operator, $operands) + protected function buildBetweenCondition($operator, $operands) { throw new NotSupportedException('Between condition is not supported by HiArt.'); } - private function buildInCondition($operator, $operands, $not = false) + protected function buildInCondition($operator, $operands, $not = false) { if (!isset($operands[0], $operands[1])) { throw new InvalidParamException("Operator '$operator' requires two operands."); @@ -251,19 +276,19 @@ private function buildInCondition($operator, $operands, $not = false) return [$key => $values]; } - private function buildNotInCondition($operator, $operands) + protected function buildNotInCondition($operator, $operands) { return $this->buildInCondition($operator, $operands, true); } - private function buildEqCondition($operator, $operands) + protected function buildEqCondition($operator, $operands) { $key = array_shift($operands); return [$key => reset($operands)]; } - private function buildNotEqCondition($operator, $operands) + protected function buildNotEqCondition($operator, $operands) { $key = array_shift($operands); From d0c80156efabcfbac441fe3f67c01070a375b4ea Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Wed, 18 Jan 2017 14:56:05 +0000 Subject: [PATCH 05/53] received answer from api, ura NOT FINISHED --- src/Command.php | 1 + src/QueryBuilder.php | 20 ++++++-------------- src/Request.php | 37 ++++++++++++++++++++++--------------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/Command.php b/src/Command.php index ac0372d..37fd42e 100644 --- a/src/Command.php +++ b/src/Command.php @@ -101,6 +101,7 @@ public function execute() Yii::endProfile($profile, __METHOD__); var_dump($response->getRequest()); var_dump($response->getData()); + var_dump($profile); die(); return $res; diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 9a77ba6..84a7f66 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -51,11 +51,8 @@ public function buildRequest($query) $this->prepare($query); $data = ['query' => $query]; - foreach (['Auth', 'Method', 'Uri', 'Headers', 'ProtocolVersion', 'UploadedFiles', 'CookieParams', 'QueryParams', 'ParsedBody'] as $name) { - $value = $this->{'build' . $name}($query); - if (!empty($value)) { - $data[$name] = $value; - } + foreach (['Auth', 'Method', 'Uri', 'Headers', 'QueryParams', 'FormParams', 'Body'] as $name) { + $data[$name] = $this->{'build' . $name}($query); } return Request::fromData($data); @@ -100,24 +97,19 @@ public function buildProtocolVersion(Query $query) return null; } - public function buildUploadedFiles(Query $query) - { - return []; - } - - public function buildCookieParams(Query $query) + public function buildQueryParams(Query $query) { return []; } - public function buildQueryParams(Query $query) + public function buildFormParams(Query $query) { return []; } - public function buildParsedBody(Query $query) + public function buildBody(Query $query) { - return []; + return null; } public function insert($table, $columns, array $options = []) diff --git a/src/Request.php b/src/Request.php index 1b0918d..282f5a6 100644 --- a/src/Request.php +++ b/src/Request.php @@ -10,12 +10,12 @@ namespace hiqdev\hiart; -use GuzzleHttp\Psr7\ServerRequest; +use GuzzleHttp\Psr7\Request as Worker; class Request { /** - * @var ServerRequest + * @var Worker */ protected $worker; @@ -24,7 +24,7 @@ class Request */ protected $query; - public function __construct(ServerRequest $worker, Query $query) + public function __construct(Worker $worker, Query $query) { $this->worker = $worker; $this->query = $query; @@ -37,21 +37,29 @@ public function getWorker() public static function fromData(array $data) { - $request = new ServerRequest($data['Method'], $data['Uri']); + $uri = $data['Uri']; - if (!empty($data['Headers'])) { - foreach ($data['Headers'] as $header => $value) { - $request = $request->withHeader($header, $value); - } + $params = $data['QueryParams']; + if (is_array($params)) { + $params = http_build_query($params, '', '&'); } + if (!empty($params)) { + $uri .= '?' . $params; + } + + $headers = $data['headers']; + $body = $data['Body']; - foreach (['ProtocolVersion', 'UploadedFiles', 'CookieParams', 'QueryParams', 'ParsedBody'] as $name) { - $value = $data[$name]; - if (!empty($value)) { - $request = $request->{'with' . $name}($value); - } + $formParams = $data['FormParams']; + if (is_array($formParams)) { + $body = http_build_query($formParams, '', '&'); + $headers['Content-Type'] = 'application/x-www-form-urlencoded'; } + $version = empty($data['ProtocolVersion']) ? '1.1' : $data['ProtocolVersion']; + + $request = new Worker($data['Method'], $uri, $headers, $body, $version); + return new static($request, $data['query']); } @@ -59,9 +67,8 @@ public function getProfile() { /// TODO serialize request object for profile $request = $this->worker; - $body = array_merge($request->getParsedBody(), $request->getQueryParams()); - return $request->getMethod() . ' ' . $request->getUri() . '#' . http_build_query($body); + return $request->getMethod() . ' ' . $request->getUri() . '#' . $request->getBody(); } public function isRaw() From 248410eca6a728f964890fc4e77666e6c96b6cb3 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Wed, 18 Jan 2017 15:46:58 +0000 Subject: [PATCH 06/53] fixed count, still redoing NOT FINISHED --- src/ActiveQuery.php | 27 ++------------------------- src/Command.php | 9 +++------ src/Query.php | 17 ++++++++++++++--- src/Request.php | 4 ++++ src/Response.php | 27 +++++++++++++++++---------- 5 files changed, 40 insertions(+), 44 deletions(-) diff --git a/src/ActiveQuery.php b/src/ActiveQuery.php index 238ef24..10d057b 100644 --- a/src/ActiveQuery.php +++ b/src/ActiveQuery.php @@ -104,7 +104,7 @@ public function createCommand($db = null) * Prepares query for use. See NOTE. * @return static */ - public function prepare() + public function prepare($builder) { // NOTE: because the same ActiveQuery may be used to build different SQL statements // (e.g. by ActiveDataProvider, one for count query, the other for row data query, @@ -210,7 +210,7 @@ private function joinWithRelation($parent, $child) } } - public function select($columns) + public function select($columns, $option = NULL) { $this->select = $columns; @@ -467,27 +467,4 @@ public function search($db = null, $options = []) return $result; } - /** - * {@inheritdoc} - */ - public function column($field, $db = null) - { - if ($field === '_id') { - $command = $this->createCommand($db); - $command->queryParts['fields'] = []; - $command->queryParts['_source'] = false; - $result = $command->search(); - if (empty($result['hits']['hits'])) { - return []; - } - $column = []; - foreach ($result['hits']['hits'] as $row) { - $column[] = $row['_id']; - } - - return $column; - } - - return parent::column($field, $db); - } } diff --git a/src/Command.php b/src/Command.php index 37fd42e..5fda38e 100644 --- a/src/Command.php +++ b/src/Command.php @@ -35,7 +35,6 @@ public function setRequest(Request $request) } /** - * XXX IN QUESTION * Sends a request to retrieve data. * In API this could be get, search or list request. * @throws ErrorResponseException @@ -43,6 +42,8 @@ public function setRequest(Request $request) */ public function search() { + $this->request->getQuery()->addAction('search'); + return $this->execute(); } @@ -99,12 +100,8 @@ public function execute() Yii::beginProfile($profile, __METHOD__); $response = $this->db->send($this->request); Yii::endProfile($profile, __METHOD__); - var_dump($response->getRequest()); - var_dump($response->getData()); - var_dump($profile); - die(); - return $res; + return $response->getData(); } } diff --git a/src/Query.php b/src/Query.php index b3dbd97..c30a391 100644 --- a/src/Query.php +++ b/src/Query.php @@ -28,6 +28,7 @@ * - body: insert or update data * - select query data * - select: fields to select + * - count: marks count query * - from: entity being queried, e.g. user * - join: data how to join with other entities * - other standard query options provided with QueryTrait: @@ -45,6 +46,8 @@ class Query extends \yii\db\Query implements QueryInterface */ public $options = []; + public $count; + public $body = []; public static function instantiate($action, $from, array $options = []) @@ -102,10 +105,9 @@ public function delete($db = null, $options = []) public function count($q = '*', $db = null) { - $options = []; - $options['count'] = 1; + $this->count = $q; - return $this->createCommand($db)->search($options); + return (int) $this->createCommand($db)->search(); } public function exists($db = null) @@ -141,6 +143,15 @@ public function action($action) return $this; } + public function addAction($action) + { + if (empty($this->action)) { + $this->action = $action; + } + + return $this; + } + public function options($options) { $this->options = $options; diff --git a/src/Request.php b/src/Request.php index 282f5a6..043a057 100644 --- a/src/Request.php +++ b/src/Request.php @@ -35,6 +35,10 @@ public function getWorker() return $this->worker; } + public function getQuery() + { + return $this->query; + } public static function fromData(array $data) { $uri = $data['Uri']; diff --git a/src/Response.php b/src/Response.php index 1bd5052..8b824f0 100644 --- a/src/Response.php +++ b/src/Response.php @@ -30,11 +30,12 @@ class Response */ protected $data; + protected $isDecoded = false; + public function __construct(ResponseInterface $worker, Request $request) { $this->worker = $worker; $this->request = $request; - $this->init(); } public function getWorker() @@ -49,15 +50,27 @@ public function getRequest() public function getData() { + if (!$this->isDecoded) { + $this->data = $this->decodeData(); + $this->isDecoded = true; + } + return $this->data; } - public function init() + public function decodeData() { - $this->data = $this->getBodyContents(); + $data = $this->getBodyContents(); if (!$this->isRaw() && $this->isJson()) { - $this->data = Json::decode($this->data); + $data = Json::decode($data); } + + return $data; + } + + public function getBodyContents() + { + return $this->worker->getBody()->getContents(); } public function isRaw() @@ -74,10 +87,4 @@ public function getHeader($name) { return $this->worker->getHeader($name); } - - public function getBodyContents() - { - return $this->worker->getBody()->getContents(); - } - } From f4b70e817b88028841dccf6ad4696e263f93c641 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Thu, 19 Jan 2017 09:00:36 +0000 Subject: [PATCH 07/53] reorganized debug into debug dir --- src/config/hisite.php | 6 +++--- src/{ => debug}/DebugAction.php | 2 +- src/{ => debug}/DebugPanel.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename src/{ => debug}/DebugAction.php (98%) rename src/{ => debug}/DebugPanel.php (99%) diff --git a/src/config/hisite.php b/src/config/hisite.php index f149e26..d088ab9 100644 --- a/src/config/hisite.php +++ b/src/config/hisite.php @@ -10,12 +10,12 @@ return [ 'modules' => array_filter([ - 'debug' => defined('YII_DEBUG') && YII_DEBUG ? [ + 'debug' => empty($params['debug.enabled']) ? null : [ 'panels' => [ 'hiart' => [ - 'class' => \hiqdev\hiart\DebugPanel::class, + 'class' => \hiqdev\hiart\debug\DebugPanel::class, ], ], - ] : null, + ], ]), ]; diff --git a/src/DebugAction.php b/src/debug/DebugAction.php similarity index 98% rename from src/DebugAction.php rename to src/debug/DebugAction.php index 6a0f122..174d09b 100644 --- a/src/DebugAction.php +++ b/src/debug/DebugAction.php @@ -8,7 +8,7 @@ * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) */ -namespace hiqdev\hiart; +namespace hiqdev\hiart\debug; use Yii; use yii\base\Action; diff --git a/src/DebugPanel.php b/src/debug/DebugPanel.php similarity index 99% rename from src/DebugPanel.php rename to src/debug/DebugPanel.php index 65431f1..94dfb5e 100644 --- a/src/DebugPanel.php +++ b/src/debug/DebugPanel.php @@ -8,7 +8,7 @@ * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) */ -namespace hiqdev\hiart; +namespace hiqdev\hiart\debug; use Yii; use yii\debug\Panel; @@ -29,7 +29,7 @@ class DebugPanel extends Panel public function init() { $this->actions['hiart-query'] = [ - 'class' => 'hiqdev\\hiart\\DebugAction', + 'class' => DebugAction::class, 'panel' => $this, 'db' => $this->db, ]; From b6234de7cb2aa26033587dc4bb4b21b1fef7c523 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Thu, 19 Jan 2017 10:53:27 +0000 Subject: [PATCH 08/53] redone debug with detail view --- src/debug/DebugPanel.php | 152 +++++++++++-------------------------- src/views/debug/detail.php | 92 ++++++++++++++++++++++ 2 files changed, 138 insertions(+), 106 deletions(-) create mode 100644 src/views/debug/detail.php diff --git a/src/debug/DebugPanel.php b/src/debug/DebugPanel.php index 94dfb5e..6e7ec29 100644 --- a/src/debug/DebugPanel.php +++ b/src/debug/DebugPanel.php @@ -10,19 +10,19 @@ namespace hiqdev\hiart\debug; +use hiqdev\hiart\Command; use Yii; -use yii\debug\Panel; +use yii\base\ViewContextInterface; use yii\helpers\ArrayHelper; use yii\helpers\Html; use yii\helpers\StringHelper; use yii\helpers\Url; use yii\log\Logger; -use yii\web\View; /** * Debugger panel that collects and displays HiArt queries performed. */ -class DebugPanel extends Panel +class DebugPanel extends \yii\debug\Panel implements ViewContextInterface { public $db = 'hiart'; @@ -77,8 +77,7 @@ public function getDetail() $apiUrl = null; $timings = $this->calculateTimings(); ArrayHelper::multisort($timings, 3, SORT_DESC); - $rows = []; - $i = 0; + // Try to get API URL try { $component = Yii::$app->get('hiart'); @@ -87,8 +86,9 @@ public function getDetail() } catch (\yii\base\InvalidConfigException $e) { // Pass } + + $rows = []; foreach ($timings as $logId => $timing) { - $duration = sprintf('%.1f ms', $timing[3] * 1000); $message = $timing[1]; $traces = $timing[4]; if (($pos = mb_strpos($message, '#')) !== false) { @@ -98,6 +98,7 @@ public function getDetail() $url = $message; $body = null; } + $traceString = ''; if (!empty($traces)) { $traceString .= Html::ul($traces, [ @@ -107,114 +108,32 @@ public function getDetail() }, ]); } + $ajaxUrl = Url::to(['hiart-query', 'logId' => $logId, 'tag' => $this->tag]); $runLink = Html::a('run query', $ajaxUrl, [ - 'class' => 'hiart-link', - 'data' => ['id' => $i], - ]) . '
'; + 'class' => 'hiart-link', + 'data' => ['id' => $logId], + ]) . '
'; + $path = preg_replace('/^[A-Z]+\s+/', '', $url); if (strpos($path, '?') !== false) { $newTabUrl = $apiUrl . rtrim($path, '&') . '&' . $body; } else { $newTabUrl = $apiUrl . $path . '?' . $body; } - $newTabLink = Html::a('to new tab', $newTabUrl, ['target' => '_blank']) . '
'; - $url_encoded = Html::encode((isset($apiUrl)) ? str_replace(' ', ' ' . $apiUrl, $url) : $url); - $body_encoded = Html::encode($body); - $rows[] = << - $duration -
$url_encoded

$body_encoded

$traceString
- $runLink$newTabLink - - - - -HTML; - ++$i; - } - $rows = implode("\n", $rows); - - Yii::$app->view->registerCss(<<<'CSS' -.string { color: green; } -.number { color: darkorange; } -.boolean { color: blue; } -.null { color: magenta; } -.key { color: red; } -CSS - ); - Yii::$app->view->registerJs(<</g, '>'); - return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { - var cls = 'number'; - if (/^"/.test(match)) { - if (/:$/.test(match)) { - cls = 'key'; - } else { - cls = 'string'; - } - } else if (/true|false/.test(match)) { - cls = 'boolean'; - } else if (/null/.test(match)) { - cls = 'null'; + $rows[] = [ + 'logId' => $logId, + 'duration' => sprintf('%.1f ms', $timing[3] * 1000), + 'traceString' => $traceString, + 'runLink' => $runLink, + 'newTabLink' => Html::a('to new tab', $newTabUrl, ['target' => '_blank']) . '
', + 'urlEncoded' => Html::encode((isset($apiUrl)) ? str_replace(' ', ' ' . $apiUrl, $url) : $url), + 'bodyEncoded' => Html::encode($body), + ]; } - return '' + match + ''; - }); -} - -$('.hiart-link').on('click', function (event) { - event.preventDefault(); - - var id = $(this).data('id'); - var result = $('.hiart-wrapper[data-id=' + id +']'); - result.find('.result').html('Sending request...'); - result.show(); - $.ajax({ - type: 'POST', - url: $(this).attr('href'), - success: function (data) { - var is_json = true; - try { - var json = JSON.parse(data.result); - } catch(e) { - is_json = false; - } - result.find('.time').html(data.time); - if (is_json) { - result.find('.result').html( syntaxHighlight( JSON.stringify( JSON.parse(data.result), undefined, 10) ) ); - } else { - result.find('.result').html( data.result ); - } - }, - error: function (jqXHR, textStatus, errorThrown) { - result.find('.time').html(''); - result.find('.result').html('Error: ' + errorThrown + ' - ' + textStatus + '
' + jqXHR.responseText); - }, - dataType: 'json' - }); - return false; -}); -JS - , View::POS_READY); - return <<HiArt Queries - - - - - - - - - - -$rows - -
TimeUrl / QueryRun Query on node
-HTML; + return $this->render('detail', compact('rows')); } private $_timings; @@ -224,6 +143,7 @@ public function calculateTimings() if ($this->_timings !== null) { return $this->_timings; } + $messages = $this->data['messages']; $timings = []; $stack = []; @@ -233,7 +153,8 @@ public function calculateTimings() if ($level === Logger::LEVEL_PROFILE_BEGIN) { $stack[] = $log; } elseif ($level === Logger::LEVEL_PROFILE_END) { - if (($last = array_pop($stack)) !== null && $last[0] === $token) { + $last = array_pop($stack); + if ($last !== null && $last[0] === $token) { $timings[$last[5]] = [count($stack), $token, $last[3], $timestamp - $last[3], $last[4]]; } } @@ -255,9 +176,28 @@ public function calculateTimings() public function save() { $target = $this->module->logTarget; - $messages = $target->filterMessages($target->messages, Logger::LEVEL_PROFILE, - ['hiqdev\hiart\Connection::handleRequest']); + $messages = $target->filterMessages($target->messages, Logger::LEVEL_PROFILE, [Command::getProfileCategory()]); return ['messages' => $messages]; } + + protected $_viewPath; + + public function setViewPath($value) + { + $this->_viewPath = $value; + } + + public function getViewPath() + { + if ($this->_viewPath === null) { + $this->_viewPath = dirname(__DIR__) . '/views/debug'; + } + return $this->_viewPath; + } + + public function render($file, $data) + { + return Yii::$app->view->render($file, $data, $this); + } } diff --git a/src/views/debug/detail.php b/src/views/debug/detail.php new file mode 100644 index 0000000..7513e81 --- /dev/null +++ b/src/views/debug/detail.php @@ -0,0 +1,92 @@ +registerCss(<<registerJs(<</g, '>'); + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { + var cls = 'number'; + if (/^"/.test(match)) { + if (/:$/.test(match)) { + cls = 'key'; + } else { + cls = 'string'; + } + } else if (/true|false/.test(match)) { + cls = 'boolean'; + } else if (/null/.test(match)) { + cls = 'null'; + } + return '' + match + ''; + }); + } + + $('.hiart-link').on('click', function (event) { + event.preventDefault(); + + var id = $(this).data('id'); + var result = $('.hiart-wrapper[data-id=' + id +']'); + result.find('.result').html('Sending request...'); + result.show(); + $.ajax({ + type: 'POST', + url: $(this).attr('href'), + success: function (data) { + var is_json = true; + try { + var json = JSON.parse(data.result); + } catch(e) { + is_json = false; + } + result.find('.time').html(data.time); + if (is_json) { + result.find('.result').html( syntaxHighlight( JSON.stringify( JSON.parse(data.result), undefined, 10) ) ); + } else { + result.find('.result').html( data.result ); + } + }, + error: function (jqXHR, textStatus, errorThrown) { + result.find('.time').html(''); + result.find('.result').html('Error: ' + errorThrown + ' - ' + textStatus + '
' + jqXHR.responseText); + }, + dataType: 'json' + }); + return false; + }); +JS +, View::POS_READY); +?> + +

HiArt Queries

+ + + + + + + + + + + + + + + + + + + + + +
TimeUrl / QueryRun Query on node

From 935c7cf212c31a75e0e54061c58b047f15472a73 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Thu, 19 Jan 2017 14:24:28 +0000 Subject: [PATCH 09/53] improved debug rendering with Timing STILL redo NOT FINISHED --- src/debug/DebugPanel.php | 110 ++++++++--------------------- src/debug/Timing.php | 135 ++++++++++++++++++++++++++++++++++++ src/views/debug/detail.php | 28 +++++--- src/views/debug/summary.php | 10 +++ 4 files changed, 191 insertions(+), 92 deletions(-) create mode 100644 src/debug/Timing.php create mode 100644 src/views/debug/summary.php diff --git a/src/debug/DebugPanel.php b/src/debug/DebugPanel.php index 6e7ec29..529daa7 100644 --- a/src/debug/DebugPanel.php +++ b/src/debug/DebugPanel.php @@ -13,10 +13,7 @@ use hiqdev\hiart\Command; use Yii; use yii\base\ViewContextInterface; -use yii\helpers\ArrayHelper; -use yii\helpers\Html; -use yii\helpers\StringHelper; -use yii\helpers\Url; +use yii\base\InvalidConfigException; use yii\log\Logger; /** @@ -31,7 +28,7 @@ public function init() $this->actions['hiart-query'] = [ 'class' => DebugAction::class, 'panel' => $this, - 'db' => $this->db, + 'db' => $this->db, ]; } @@ -48,25 +45,17 @@ public function getName() */ public function getSummary() { - $timings = $this->calculateTimings(); - $queryCount = count($timings); - $queryTime = 0; + $timings = $this->getTimings(); + $total = 0; foreach ($timings as $timing) { - $queryTime += $timing[3]; + $total += $timing[3]; } - $queryTime = number_format($queryTime * 1000) . ' ms'; - $url = $this->getUrl(); - $output = << - - HiArt - $queryCount - $queryTime - - -HTML; - - return $queryCount > 0 ? $output : ''; + + return $this->render('summary', [ + 'url' => $this->getUrl(), + 'count' => count($timings), + 'total' => number_format($total * 1000) . ' ms', + ]); } /** @@ -74,76 +63,33 @@ public function getSummary() */ public function getDetail() { - $apiUrl = null; - $timings = $this->calculateTimings(); - ArrayHelper::multisort($timings, 3, SORT_DESC); + return $this->render('detail', [ + 'timings' => Timing::buildAll($this), + ]); + } - // Try to get API URL + public function getBaseUri($dbname) + { try { - $component = Yii::$app->get('hiart'); - $apiUrl = (StringHelper::endsWith($component->config['base_uri'], - '/')) ? $component->config['base_uri'] : $component->config['base_uri'] . '/'; - } catch (\yii\base\InvalidConfigException $e) { - // Pass + return Yii::$app->get($dbname)->getBaseUri(); + } catch (InvalidConfigException $e) { + return null; } + } - $rows = []; - foreach ($timings as $logId => $timing) { - $message = $timing[1]; - $traces = $timing[4]; - if (($pos = mb_strpos($message, '#')) !== false) { - $url = mb_substr($message, 0, $pos); - $body = mb_substr($message, $pos + 1); - } else { - $url = $message; - $body = null; - } - - $traceString = ''; - if (!empty($traces)) { - $traceString .= Html::ul($traces, [ - 'class' => 'trace', - 'item' => function ($trace) { - return "
  • {$trace['file']}({$trace['line']})
  • "; - }, - ]); - } - - $ajaxUrl = Url::to(['hiart-query', 'logId' => $logId, 'tag' => $this->tag]); - $runLink = Html::a('run query', $ajaxUrl, [ - 'class' => 'hiart-link', - 'data' => ['id' => $logId], - ]) . '
    '; - - $path = preg_replace('/^[A-Z]+\s+/', '', $url); - if (strpos($path, '?') !== false) { - $newTabUrl = $apiUrl . rtrim($path, '&') . '&' . $body; - } else { - $newTabUrl = $apiUrl . $path . '?' . $body; - } + private $_timings; - $rows[] = [ - 'logId' => $logId, - 'duration' => sprintf('%.1f ms', $timing[3] * 1000), - 'traceString' => $traceString, - 'runLink' => $runLink, - 'newTabLink' => Html::a('to new tab', $newTabUrl, ['target' => '_blank']) . '
    ', - 'urlEncoded' => Html::encode((isset($apiUrl)) ? str_replace(' ', ' ' . $apiUrl, $url) : $url), - 'bodyEncoded' => Html::encode($body), - ]; + public function getTimings() + { + if ($this->_timings === null) { + $this->_timings = $this->calculateTimings(); } - return $this->render('detail', compact('rows')); + return $this->_timings; } - private $_timings; - public function calculateTimings() { - if ($this->_timings !== null) { - return $this->_timings; - } - $messages = $this->data['messages']; $timings = []; $stack = []; @@ -167,7 +113,7 @@ public function calculateTimings() } ksort($timings); - return $this->_timings = $timings; + return $timings; } /** diff --git a/src/debug/Timing.php b/src/debug/Timing.php new file mode 100644 index 0000000..17fd5cd --- /dev/null +++ b/src/debug/Timing.php @@ -0,0 +1,135 @@ +panel = $panel; + $this->logId = $logId; + } + + public static function buildAll(DebugPanel $panel) + { + $rawTimings = $panel->getTimings(); + ArrayHelper::multisort($rawTimings, 3, SORT_DESC); + + $timings = []; + foreach ($rawTimings as $logId => $rawTiming) { + $timings[] = static::buildOne($panel, $logId, $rawTiming); + } + + return $timings; + } + + public static function buildOne($panel, $logId, $rawTiming) + { + $new = new static($panel, $logId); + $new->updateFromRaw($rawTiming); + + return $new; + } + + public function updateFromRaw($rawTiming) + { + $this->duration = $rawTiming[3]; + $this->traces = $rawTiming[4]; + $profile = $rawTiming[1]; + + foreach (Request::decodeProfile($profile) as $key => $value) { + $this->{$key} = $value; + } + } + + public function getLogId() + { + return $this->logId; + } + + public function getMethod() + { + return $this->method; + } + + public function getUrlEncoded() + { + return Html::encode($this->getFullUri()); + } + + public function getBodyEncoded() + { + return Html::encode($this->body); + } + + public function getDuration() + { + return sprintf('%.1f ms', $this->duration * 1000); + } + + public function getTrace() + { + $result = ''; + if (!empty($this->traces)) { + $result .= Html::ul($this->traces, [ + 'class' => 'trace', + 'item' => function ($trace) { + return "
  • {$trace['file']}({$trace['line']})
  • "; + }, + ]); + } + + return $result; + } + + public function getRunLink() + { + $ajaxUrl = Url::to(['hiart-query', 'logId' => $this->logId, 'tag' => $this->panel->tag]); + + return Html::a('run query', $ajaxUrl, [ + 'class' => 'hiart-link', + 'data' => ['id' => $this->logId], + ]); + } + + public function getNewTabLink() + { + $sign = strpos($this->uri, '?') === false ? '?' : ''; + $newTabUrl = rtrim($this->getFullUri(), '&') . $sign . $this->body; + + return Html::a('to new tab', $newTabUrl, ['target' => '_blank']); + } + + public function getFullUri() + { + return $this->getBaseUri() . '/'. $this->uri; + } + + public function getBaseUri() + { + $this->panel->getBaseUri($this->dbname); + } +} diff --git a/src/views/debug/detail.php b/src/views/debug/detail.php index 7513e81..b799b09 100644 --- a/src/views/debug/detail.php +++ b/src/views/debug/detail.php @@ -8,6 +8,7 @@ .boolean { color: blue; } .null { color: magenta; } .key { color: red; } + .white-space-normal { white-space: normal; } CSS ); @@ -57,7 +58,7 @@ function syntaxHighlight(json) { }, error: function (jqXHR, textStatus, errorThrown) { result.find('.time').html(''); - result.find('.result').html('Error: ' + errorThrown + ' - ' + textStatus + '
    ' + jqXHR.responseText); + result.find('.result').html('Error: ' + errorThrown + ' - ' + textStatus + '
    ' + jqXHR.responseText); }, dataType: 'json' }); @@ -69,22 +70,29 @@ function syntaxHighlight(json) {

    HiArt Queries

    - +
    - - - + + + - + - - - + + + - + diff --git a/src/views/debug/summary.php b/src/views/debug/summary.php new file mode 100644 index 0000000..1148407 --- /dev/null +++ b/src/views/debug/summary.php @@ -0,0 +1,10 @@ + + From aba9fcaf0eeb847e920f55fa3bdfc4b31d01ecbb Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Thu, 19 Jan 2017 16:13:58 +0000 Subject: [PATCH 10/53] fixed DebugAction --- src/debug/DebugAction.php | 61 ++++++++------------------------------- src/debug/DebugPanel.php | 3 -- src/debug/Timing.php | 26 +++++++---------- 3 files changed, 22 insertions(+), 68 deletions(-) diff --git a/src/debug/DebugAction.php b/src/debug/DebugAction.php index 174d09b..978cf46 100644 --- a/src/debug/DebugAction.php +++ b/src/debug/DebugAction.php @@ -11,7 +11,6 @@ namespace hiqdev\hiart\debug; use Yii; -use yii\base\Action; use yii\base\NotSupportedException; use yii\helpers\ArrayHelper; use yii\web\HttpException; @@ -20,16 +19,13 @@ /** * Debug Action is used by [[DebugPanel]] to perform HiArt queries using ajax. */ -class DebugAction extends Action +class DebugAction extends \yii\base\Action { - /** - * @var string the connection id to use - */ - public $db; /** * @var DebugPanel */ public $panel; + /** * @var \yii\debug\controllers\DefaultController */ @@ -39,59 +35,26 @@ public function run($logId, $tag) { $this->controller->loadData($tag); - $timings = $this->panel->calculateTimings(); + $timings = $this->panel->getTimings(); ArrayHelper::multisort($timings, 3, SORT_DESC); + if (!isset($timings[$logId])) { throw new HttpException(404, 'Log message not found.'); } - $message = $timings[$logId][1]; - if (($pos = mb_strpos($message, '#')) !== false) { - $url = mb_substr($message, 0, $pos); - $body = mb_substr($message, $pos + 1); - } else { - $url = $message; - $body = null; - } - $method = mb_substr($url, 0, $pos = mb_strpos($url, ' ')); - $url = mb_substr($url, $pos + 1); - parse_str($body, $options); - - /* @var $db Connection */ - $db = Yii::$app->get($this->db); - $time = microtime(true); - switch ($method) { - case 'GET': - $result = $db->get($url, $options, true); - break; - case 'POST': - $result = $db->post($url, $options, $body, true); - break; - case 'PUT': - $result = $db->put($url, $options, $body, true); - break; - case 'DELETE': - $result = $db->delete($url, $options, $body, true); - break; - case 'HEAD': - $result = $db->head($url, $options, $body); - break; - default: - throw new NotSupportedException("Request method '$method' is not supported by HiArt."); - } - $time = microtime(true) - $time; - - if ($result === true) { - $result = 'success'; - } elseif ($result === false) { - $result = 'no success'; - } + $request = unserialize($timings[$logId][1]); + var_dump($request); + $db = Yii::$app->get($request->getDbname()); + $time = microtime(true); + $response = $db->send($request); + var_dump($response->getBodyContents()); + $time = microtime(true) - $time; Yii::$app->response->format = Response::FORMAT_JSON; return [ 'time' => sprintf('%.1f ms', $time * 1000), - 'result' => $result, + 'result' => var_dump($response->getData()), ]; } } diff --git a/src/debug/DebugPanel.php b/src/debug/DebugPanel.php index 529daa7..880df12 100644 --- a/src/debug/DebugPanel.php +++ b/src/debug/DebugPanel.php @@ -21,14 +21,11 @@ */ class DebugPanel extends \yii\debug\Panel implements ViewContextInterface { - public $db = 'hiart'; - public function init() { $this->actions['hiart-query'] = [ 'class' => DebugAction::class, 'panel' => $this, - 'db' => $this->db, ]; } diff --git a/src/debug/Timing.php b/src/debug/Timing.php index 17fd5cd..7007f92 100644 --- a/src/debug/Timing.php +++ b/src/debug/Timing.php @@ -22,10 +22,7 @@ class Timing protected $logId; protected $duration; protected $traces; - protected $dbname; - protected $method; - protected $uri; - protected $body; + protected $request; public function __construct(DebugPanel $panel, $logId) { @@ -56,13 +53,9 @@ public static function buildOne($panel, $logId, $rawTiming) public function updateFromRaw($rawTiming) { + $this->request = unserialize($rawTiming[1]); $this->duration = $rawTiming[3]; - $this->traces = $rawTiming[4]; - $profile = $rawTiming[1]; - - foreach (Request::decodeProfile($profile) as $key => $value) { - $this->{$key} = $value; - } + $this->traces = $rawTiming[4]; } public function getLogId() @@ -72,7 +65,7 @@ public function getLogId() public function getMethod() { - return $this->method; + return $this->request->getMethod(); } public function getUrlEncoded() @@ -82,7 +75,7 @@ public function getUrlEncoded() public function getBodyEncoded() { - return Html::encode($this->body); + return Html::encode($this->request->getBody()); } public function getDuration() @@ -117,19 +110,20 @@ public function getRunLink() public function getNewTabLink() { - $sign = strpos($this->uri, '?') === false ? '?' : ''; - $newTabUrl = rtrim($this->getFullUri(), '&') . $sign . $this->body; + $uri = $this->getFullUri(); + $sign = strpos($uri, '?') === false ? '?' : ''; + $newTabUrl = rtrim($uri, '&') . $sign . $this->request->getBody(); return Html::a('to new tab', $newTabUrl, ['target' => '_blank']); } public function getFullUri() { - return $this->getBaseUri() . '/'. $this->uri; + return $this->getBaseUri() . '/'. $this->request->getUri(); } public function getBaseUri() { - $this->panel->getBaseUri($this->dbname); + return $this->panel->getBaseUri($this->request->getDbname()); } } From 43692060533c24cd50d59aff526520f8c2927f44 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Thu, 19 Jan 2017 16:14:59 +0000 Subject: [PATCH 11/53] + serialization in Request --- src/Request.php | 157 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 136 insertions(+), 21 deletions(-) diff --git a/src/Request.php b/src/Request.php index 043a057..8955c2f 100644 --- a/src/Request.php +++ b/src/Request.php @@ -12,8 +12,10 @@ use GuzzleHttp\Psr7\Request as Worker; -class Request +class Request implements \Serializable { + protected $builder; + /** * @var Worker */ @@ -24,14 +26,65 @@ class Request */ protected $query; - public function __construct(Worker $worker, Query $query) + /** + * @var string Connection name + */ + protected $dbname; + + /** + * @var array request method + */ + protected $method; + protected $uri; + protected $headers = []; + protected $body; + protected $version; + + public function __construct(QueryBuilder $builder, Query $query) { - $this->worker = $worker; + $this->builder = $builder; $this->query = $query; } + public function getDbname() + { + return $this->dbname; + } + + public function getMethod() + { + return $this->method; + } + + public function getUri() + { + return $this->uri; + } + + public function getHeaders() + { + return $this->headers; + } + + public function getBody() + { + return $this->body; + } + + public function getVersion() + { + return $this->version; + } + public function getWorker() { + if ($this->worker === null) { + if (!empty($this->query)) { + $this->updateFromQuery(); + } + $this->worker = $this->createWorker(); + } + return $this->worker; } @@ -39,40 +92,102 @@ public function getQuery() { return $this->query; } - public static function fromData(array $data) + + protected function updateFromQuery() + { + $this->builder->prepare($this->query); + + $this->buildDbname(); + $this->buildAuth(); + $this->buildMethod(); + $this->buildUri(); + $this->buildQueryParams(); + $this->buildHeaders(); + $this->buildBody(); + $this->buildFormParams(); + $this->buildProtocolVersion(); + } + + protected function createWorker() { - $uri = $data['Uri']; + return new Worker($this->method, $this->uri, $this->headers, $this->body, $this->version); + } - $params = $data['QueryParams']; + protected function buildDbname() + { + $this->dbname = $this->builder->db->name; + } + + protected function buildAuth() + { + $this->builder->buildAuth($this->query); + } + + protected function buildMethod() + { + $this->method = $this->builder->buildMethod($this->query) ?: 'GET'; + } + + protected function buildUri() + { + $this->uri = $this->builder->buildUri($this->query); + } + + protected function buildQueryParams() + { + $params = $this->builder->buildQueryParams($this->query); if (is_array($params)) { $params = http_build_query($params, '', '&'); } if (!empty($params)) { - $uri .= '?' . $params; + $this->uri .= '?' . $params; } + } - $headers = $data['headers']; - $body = $data['Body']; + protected function buildHeaders() + { + $this->headers = $this->builder->buildHeaders($this->query); + } - $formParams = $data['FormParams']; - if (is_array($formParams)) { - $body = http_build_query($formParams, '', '&'); - $headers['Content-Type'] = 'application/x-www-form-urlencoded'; - } + protected function buildBody() + { + $this->body = $this->builder->buildBody($this->query); + } - $version = empty($data['ProtocolVersion']) ? '1.1' : $data['ProtocolVersion']; + protected function buildFormParams() + { + $this->setFormParams($this->builder->buildFormParams($this->query)); + } - $request = new Worker($data['Method'], $uri, $headers, $body, $version); + protected function setFormParams($params) + { + if (!empty($params)) { + $this->body = is_array($params) ? http_build_query($params, '', '&') : $params; + $this->headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + } - return new static($request, $data['query']); + protected function buildProtocolVersion() + { + $this->version = $this->builder->buildProtocolVersion($this->query) ?: '1.1'; } - public function getProfile() + public function serialize() { - /// TODO serialize request object for profile - $request = $this->worker; + $this->getWorker(); + $data = []; + foreach (['dbname', 'method', 'uri', 'headers', 'body', 'version'] as $key) { + $data[$key] = $this->{$key}; + } - return $request->getMethod() . ' ' . $request->getUri() . '#' . $request->getBody(); + return serialize($data); + } + + public function unserialize($string) + { + foreach (unserialize($string) as $key => $value) { + $this->{$key} = $value; + } } public function isRaw() From bfd8671488e12e5251da6a1e91c2c80ff516d55a Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Thu, 19 Jan 2017 16:17:21 +0000 Subject: [PATCH 12/53] refactoring NOT FINISHED --- src/ActiveQuery.php | 17 ++++++--------- src/Command.php | 21 +++++++++++++----- src/Connection.php | 14 ++++++++++-- src/Query.php | 40 ++++++++++++++++------------------ src/QueryBuilder.php | 46 ++++++++++++++++++++++++--------------- src/debug/DebugAction.php | 4 +--- 6 files changed, 83 insertions(+), 59 deletions(-) diff --git a/src/ActiveQuery.php b/src/ActiveQuery.php index 10d057b..9b01a42 100644 --- a/src/ActiveQuery.php +++ b/src/ActiveQuery.php @@ -17,10 +17,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface { - use ActiveQueryTrait { - createModels as defaultCreateModels; - } - + use ActiveQueryTrait; use ActiveRelationTrait; /** @@ -249,7 +246,7 @@ public function all($db = null) return parent::all($db); } - $rows = $this->createCommand($db)->search($this->options); + $rows = $this->createCommand($db)->search(); return $this->populate($rows); } @@ -405,9 +402,7 @@ private function addInverseRelation($relatedModel) */ public function one($db = null) { - // $result = $this->createCommand($db)->get(); - - $result = $this->createCommand($db)->search(ArrayHelper::merge(['limit' => 1], $this->options)); + $result = $this->limit(1)->search($db); if (empty($result)) { return null; } @@ -428,6 +423,7 @@ public function one($db = null) // $this->findWith($this->with, $models); // $model = $models[0]; // } + return $result; } @@ -449,9 +445,10 @@ public function one($db = null) /** * {@inheritdoc} */ - public function search($db = null, $options = []) + public function OLD_search($db = null, $options = []) { - $result = $this->createCommand($db)->search($options); + $result = parent::search($db, $options); + // TODO implement with() for asArray if (!empty($result) && !$this->asArray) { $models = $this->createModels($result); diff --git a/src/Command.php b/src/Command.php index 5fda38e..07e06d1 100644 --- a/src/Command.php +++ b/src/Command.php @@ -42,7 +42,7 @@ public function setRequest(Request $request) */ public function search() { - $this->request->getQuery()->addAction('search'); + $this->request->getQuery()->addAction('search')->addBatch(true); return $this->execute(); } @@ -51,7 +51,7 @@ public function search() * Sends a request to create/insert data. * @param mixed $table entity to create * @param mixed $columns attributes of object to create - * @return mixed + * @return $this */ public function insert($table, $columns, array $options = []) { @@ -60,6 +60,12 @@ public function insert($table, $columns, array $options = []) return $this->setRequest($request); } + /** + * Sends a request to create/insert data. + * @param mixed $table entity to create + * @param mixed $columns attributes of object to create + * @return $this + */ public function update($table, $columns, $condition = [], array $options = []) { $request = $this->db->getQueryBuilder()->update($table, $columns, $condition, $options); @@ -96,12 +102,17 @@ public function perform($action, $table, $body = [], array $options = []) */ public function execute() { - $profile = $this->request->getProfile(); - Yii::beginProfile($profile, __METHOD__); + $profile = serialize($this->request); + $category = static::getProfileCategory(); + Yii::beginProfile($profile, $category); $response = $this->db->send($this->request); - Yii::endProfile($profile, __METHOD__); + Yii::endProfile($profile, $category); return $response->getData(); } + public static function getProfileCategory() + { + return __METHOD__; + } } diff --git a/src/Connection.php b/src/Connection.php index ce7b5d4..88d2cc5 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -37,10 +37,14 @@ class Connection extends Component { const EVENT_AFTER_OPEN = 'afterOpen'; + public $commandClass = Command::class; + public $queryClass = Query::class; public $activeQueryClass = ActiveQuery::class; + public $name = 'hiart'; + /** * @var array Config */ @@ -119,6 +123,11 @@ public function init() } } + public function getBaseUri() + { + return $this->config['base_uri']; + } + /** * Closes the connection when this component is being serialized. * @return array @@ -142,11 +151,11 @@ public function getDriverName() * @param array $config the configuration for the Command class * @return Command the DB command */ - public function createCommand($config = []) + public function createCommand(array $config = []) { $config['db'] = $this; - return new Command($config); + return new $this->commandClass($config); } /** @@ -170,6 +179,7 @@ public function createQueryBuilder() return new QueryBuilder($this); } + /** * Performs GET HTTP request. * @param string $url URL diff --git a/src/Query.php b/src/Query.php index c30a391..924a830 100644 --- a/src/Query.php +++ b/src/Query.php @@ -70,7 +70,7 @@ public function createCommand($db = null) public function one($db = null) { - $result = $this->createCommand($db)->search(['limit' => 1]); + $result = $this->limit(1)->createCommand($db)->search(); if (empty($result)) { return false; } @@ -79,9 +79,10 @@ public function one($db = null) return $record; } - public function search($db = null, $options = []) + public function search($db = null) { - $result = $this->createCommand($db)->search($options); + $result = $this->createCommand($db)->search(); + if (!empty($result) && $this->indexBy !== null) { $rows = []; foreach ($result as $key => $row) { @@ -115,50 +116,47 @@ public function exists($db = null) return self::one($db) !== false; } - public function stats($groups) + public function action($action) { - $this->stats = $groups; + $this->action = $action; return $this; } - public function highlight($highlight) + public function addAction($action) { - $this->highlight = $highlight; + if (empty($this->action)) { + $this->action = $action; + } return $this; } - public function addAggregation($name, $type, $options) + public function addBatch($batch) { - $this->aggregations[$name] = [$type => $options]; + if (!isset($this->options['batch'])) { + $this->options['batch'] = $batch; + } return $this; } - public function action($action) + public function options($options) { - $this->action = $action; + $this->options = $options; return $this; } - public function addAction($action) + public function addOptions($options) { - if (empty($this->action)) { - $this->action = $action; + if (!empty($options)) { + $this->options = array_merge($this->options, $options); } return $this; } - public function options($options) - { - $this->options = $options; - - return $this; - } - public function body($body) { $this->body = $body; diff --git a/src/QueryBuilder.php b/src/QueryBuilder.php index 84a7f66..996f1b8 100644 --- a/src/QueryBuilder.php +++ b/src/QueryBuilder.php @@ -28,34 +28,30 @@ public function __construct($connection, $config = []) } /** + * Builds config array to create Command. * @param Query $query * @throws NotSupportedException * @return array */ public function build(Query $query) { - return ['request' => $this->buildRequest($query)]; + return ['request' => $this->createRequest($query)]; + } + + public function createRequest($query) + { + return new Request($this, $query); } /** - * Prepares query. This is function for you to redefine. + * Prepares query before actual building. + * This function for you to redefine. + * It will be called before other build functions. * @param Query $query */ public function prepare(Query $query) { - $query->prepare($this); - } - - public function buildRequest($query) - { - $this->prepare($query); - - $data = ['query' => $query]; - foreach (['Auth', 'Method', 'Uri', 'Headers', 'QueryParams', 'FormParams', 'Body'] as $name) { - $data[$name] = $this->{'build' . $name}($query); - } - - return Request::fromData($data); + return $query->prepare($this); } /** @@ -112,30 +108,44 @@ public function buildBody(Query $query) return null; } + /** + * Creates insert request. + * @param string $table + * @param array $columns + * @param array $options + * @return Request + */ public function insert($table, $columns, array $options = []) { return $this->perform('insert', $table, $columns, $options); } + /** + * Creates update request. + * @param string $table + * @param array $columns + * @param array $options + * @return Request + */ public function update($table, $columns, $condition = [], array $options = []) { $query = $this->createQuery('update', $table, $options)->body($columns)->where($condition); - return $this->buildRequest($query); + return $this->createRequest($query); } public function delete($table, $condition = [], array $options = []) { $query = $this->createQuery('delete', $table, $options)->where($condition); - return $this->buildRequest($query); + return $this->createRequest($query); } public function perform($action, $table, $body, $options = []) { $query = $this->createQuery($action, $table, $options)->body($body); - return $this->buildRequest($query); + return $this->createRequest($query); } public function createQuery($action, $table, array $options = []) diff --git a/src/debug/DebugAction.php b/src/debug/DebugAction.php index 978cf46..9d81899 100644 --- a/src/debug/DebugAction.php +++ b/src/debug/DebugAction.php @@ -43,18 +43,16 @@ public function run($logId, $tag) } $request = unserialize($timings[$logId][1]); - var_dump($request); $db = Yii::$app->get($request->getDbname()); $time = microtime(true); $response = $db->send($request); - var_dump($response->getBodyContents()); $time = microtime(true) - $time; Yii::$app->response->format = Response::FORMAT_JSON; return [ 'time' => sprintf('%.1f ms', $time * 1000), - 'result' => var_dump($response->getData()), + 'result' => $response->getData(), ]; } } From 9b24ea4a572fcc946caee12c7c988a6d1619dca8 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Thu, 19 Jan 2017 19:18:18 +0000 Subject: [PATCH 13/53] simplified indexBy and createModels --- src/ActiveQuery.php | 140 ++++++++++---------------------------------- src/Command.php | 2 +- src/Query.php | 55 +++++++++-------- 3 files changed, 64 insertions(+), 133 deletions(-) diff --git a/src/ActiveQuery.php b/src/ActiveQuery.php index 9b01a42..a672a29 100644 --- a/src/ActiveQuery.php +++ b/src/ActiveQuery.php @@ -101,7 +101,7 @@ public function createCommand($db = null) * Prepares query for use. See NOTE. * @return static */ - public function prepare($builder) + public function prepare($builder = null) { // NOTE: because the same ActiveQuery may be used to build different SQL statements // (e.g. by ActiveDataProvider, one for count query, the other for row data query, @@ -233,6 +233,22 @@ public function addSelect($columns) return $this; } + /** + * Executes query and returns a single row of result. + * + * @param Connection $db the DB connection used to create the DB command. + * If null, the DB connection returned by [[modelClass]] will be used. + * @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]], + * the query result may be either an array or an ActiveRecord object. Null will be returned + * if the query results in nothing. + */ + public function one($db = null) + { + $row = parent::one($db); + + return $this->asArray ? $row : reset($this->populate([$row])); + } + /** * Executes query and returns all results as an array. * @param Connection $db the DB connection used to create the DB command. @@ -242,11 +258,10 @@ public function addSelect($columns) public function all($db = null) { if ($this->asArray) { - // TODO implement with return parent::all($db); } - $rows = $this->createCommand($db)->search(); + $rows = $this->searchAll(); return $this->populate($rows); } @@ -258,6 +273,7 @@ public function populate($rows) } $models = $this->createModels($rows); + if (!empty($this->with)) { $this->findWith($this->with, $models); } @@ -272,42 +288,21 @@ public function populate($rows) private function createModels($rows) { $models = []; - if ($this->asArray) { - if ($this->indexBy === null) { - return $rows; - } - foreach ($rows as $row) { - if (is_string($this->indexBy)) { - $key = $row[$this->indexBy]; + $class = $this->modelClass; + foreach ($rows as $row) { + $model = $class::instantiate($row); + $modelClass = get_class($model); + $modelClass::populateRecord($model, $row); + $this->populateJoinedRelations($model, $row); + if ($this->indexBy) { + if ($this->indexBy instanceof \Closure) { + $key = call_user_func($this->indexBy, $model); } else { - $key = call_user_func($this->indexBy, $row); - } - $models[$key] = $row; - } - } else { - /* @var $class ActiveRecord */ - $class = $this->modelClass; - if ($this->indexBy === null) { - foreach ($rows as $row) { - $model = $class::instantiate($row); - $modelClass = get_class($model); - $modelClass::populateRecord($model, $row); - $this->populateJoinedRelations($model, $row); - $models[] = $model; + $key = $model->{$this->indexBy}; } + $models[$key] = $model; } else { - foreach ($rows as $row) { - $model = $class::instantiate($row); - $modelClass = get_class($model); - $modelClass::populateRecord($model, $row); - $this->populateJoinedRelations($model, $row); - if (is_string($this->indexBy)) { - $key = $model->{$this->indexBy}; - } else { - $key = call_user_func($this->indexBy, $model); - } - $models[$key] = $model; - } + $models[] = $model; } } @@ -391,77 +386,4 @@ private function addInverseRelation($relatedModel) $relatedModel->populateRelation($this->inverseOf, $inverseRelation->multiple ? [$this->primaryModel] : $this->primaryModel); } - /** - * Executes query and returns a single row of result. - * - * @param Connection $db the DB connection used to create the DB command. - * If null, the DB connection returned by [[modelClass]] will be used. - * @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]], - * the query result may be either an array or an ActiveRecord object. Null will be returned - * if the query results in nothing. - */ - public function one($db = null) - { - $result = $this->limit(1)->search($db); - if (empty($result)) { - return null; - } - $result = reset($result); - - if ($this->asArray) { - // TODO implement with() -// /* @var $modelClass ActiveRecord */ -// $modelClass = $this->modelClass; -// $model = $result['_source']; -// $pk = $modelClass::primaryKey()[0]; -// if ($pk === '_id') { -// $model['_id'] = $result['_id']; -// } -// $model['_score'] = $result['_score']; -// if (!empty($this->with)) { -// $models = [$model]; -// $this->findWith($this->with, $models); -// $model = $models[0]; -// } - - return $result; - } - - /* @var $class ActiveRecord */ - $class = $this->modelClass; - $model = $class::instantiate($result); - $class::populateRecord($model, $result); - $this->populateJoinedRelations($model, $result); - if (!empty($this->with)) { - $models = [$model]; - $this->findWith($this->with, $models); - $model = $models[0]; - } - $model->afterFind(); - - return $model; - } - - /** - * {@inheritdoc} - */ - public function OLD_search($db = null, $options = []) - { - $result = parent::search($db, $options); - - // TODO implement with() for asArray - if (!empty($result) && !$this->asArray) { - $models = $this->createModels($result); - if (!empty($this->with)) { - $this->findWith($this->with, $models); - } - foreach ($models as $model) { - $model->afterFind(); - } - $result = $models; - } - - return $result; - } - } diff --git a/src/Command.php b/src/Command.php index 07e06d1..4b78367 100644 --- a/src/Command.php +++ b/src/Command.php @@ -42,7 +42,7 @@ public function setRequest(Request $request) */ public function search() { - $this->request->getQuery()->addAction('search')->addBatch(true); + $this->request->getQuery()->addAction('search'); return $this->execute(); } diff --git a/src/Query.php b/src/Query.php index 924a830..3d48e00 100644 --- a/src/Query.php +++ b/src/Query.php @@ -70,33 +70,37 @@ public function createCommand($db = null) public function one($db = null) { - $result = $this->limit(1)->createCommand($db)->search(); - if (empty($result)) { - return false; - } - $record = reset($result); - - return $record; + return $this->limit(1)->addOption('batch', false)->search(); } - public function search($db = null) + public function all($db = null) { - $result = $this->createCommand($db)->search(); + $rows = $this->searchAll(); - if (!empty($result) && $this->indexBy !== null) { - $rows = []; - foreach ($result as $key => $row) { - if (is_string($this->indexBy)) { - $key = isset($row['fields'][$this->indexBy]) ? $row['fields'][$this->indexBy] : $row['_source'][$this->indexBy]; - } else { + if (!empty($rows) && $this->indexBy !== null) { + $result = []; + foreach ($rows as $row) { + if ($this->indexBy instanceof \Closure) { $key = call_user_func($this->indexBy, $row); + } else { + $key = $row[$this->indexBy]; } - $rows[$key] = $row; + $result[$key] = $row; } - $result = $rows; + $rows = $result; } - return $result; + return $rows; + } + + public function searchAll($db = null) + { + return $this->addOption('batch', true)->search(); + } + + public function search($db = null) + { + return $this->createCommand($db)->search(); } public function delete($db = null, $options = []) @@ -108,12 +112,12 @@ public function count($q = '*', $db = null) { $this->count = $q; - return (int) $this->createCommand($db)->search(); + return (int) $this->search(); } public function exists($db = null) { - return self::one($db) !== false; + return !empty(self::one($db)); } public function action($action) @@ -132,15 +136,20 @@ public function addAction($action) return $this; } - public function addBatch($batch) + public function addOption($name, $value) { - if (!isset($this->options['batch'])) { - $this->options['batch'] = $batch; + if (!isset($this->options[$name])) { + $this->options[$name] = $value; } return $this; } + public function getOption($name) + { + return isset($this->options[$name]) ? $this->options[$name] : null; + } + public function options($options) { $this->options = $options; From e31da56adc720c728311cdc0532e9c099f56793f Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Fri, 20 Jan 2017 10:00:26 +0000 Subject: [PATCH 14/53] fixed JS for run query result --- src/views/debug/detail.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/views/debug/detail.php b/src/views/debug/detail.php index b799b09..fb0b330 100644 --- a/src/views/debug/detail.php +++ b/src/views/debug/detail.php @@ -42,19 +42,9 @@ function syntaxHighlight(json) { $.ajax({ type: 'POST', url: $(this).attr('href'), - success: function (data) { - var is_json = true; - try { - var json = JSON.parse(data.result); - } catch(e) { - is_json = false; - } + success: function (data, status, xhr) { + result.find('.result').html( syntaxHighlight( JSON.stringify( data.result, undefined, 10) ) ); result.find('.time').html(data.time); - if (is_json) { - result.find('.result').html( syntaxHighlight( JSON.stringify( JSON.parse(data.result), undefined, 10) ) ); - } else { - result.find('.result').html( data.result ); - } }, error: function (jqXHR, textStatus, errorThrown) { result.find('.time').html(''); From 920c928279aa9636af7743ddaf192451fc193917 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Fri, 20 Jan 2017 10:50:32 +0000 Subject: [PATCH 15/53] fixed Query::count --- src/Query.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Query.php b/src/Query.php index 3d48e00..4a03f02 100644 --- a/src/Query.php +++ b/src/Query.php @@ -112,7 +112,7 @@ public function count($q = '*', $db = null) { $this->count = $q; - return (int) $this->search(); + return (int) $this->searchAll(); } public function exists($db = null) From d45d933ba509415cd36891b7aed28d0cf6b5c122 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Fri, 20 Jan 2017 16:33:21 +0000 Subject: [PATCH 16/53] more fixes STILL redo NOT FINISHED --- src/ActiveRecord.php | 38 ++++++++++++----------- src/Collection.php | 72 ++++++++------------------------------------ src/Command.php | 4 ++- src/Connection.php | 17 ++++++++++- 4 files changed, 53 insertions(+), 78 deletions(-) diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index ecf31af..e660898 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -233,7 +233,7 @@ public function insert($runValidation = true, $attributes = null, $options = []) $values = $this->getDirtyAttributes($attributes); $data = array_merge($values, $options, ['id' => $this->getOldPrimaryKey()]); - $result = $this->perform('insert', $data); + $result = $this->performScenario('insert', $data); $pk = static::primaryKey()[0]; $this->$pk = $result['id']; @@ -257,7 +257,7 @@ public function delete($options = []) } $data = array_merge($options, ['id' => $this->getOldPrimaryKey()]); - $result = $this->perform('delete', $data); + $result = $this->performScenario('delete', $data); $this->setOldAttributes(null); $this->afterDelete(); @@ -287,8 +287,7 @@ protected function updateInternal($attributes = null, $options = []) return 0; } - $data = array_merge($values, $options, ['id' => $this->getOldPrimaryKey()]); - $result = $this->perform('update', $data); + $result = $this->performScenario('update', $values, $options); $changedAttributes = []; foreach ($values as $name => $value) { @@ -301,21 +300,26 @@ protected function updateInternal($attributes = null, $options = []) return $result === false ? false : true; } - protected function perform($defaultScenario, $data, $batch = false) + public function performScenario($defaultScenario, $data, array $options = []) { - $command = $this->getScenarioCommand($defaultScenario); + $action = $this->getScenarioAction($defaultScenario); - return static::getDb()->createCommand()->perform($command, static::from(), $data, compact('batch')); + return static::perform($action, $data, $options); + } + + public static function perform($action, $data, array $options = []) + { + return static::getDb()->createCommand()->perform($action, static::from(), $data, $options); } /** - * Creates command name from the current scenario name. - * @param string $default default command name + * Converts scenario name to action. + * @param string $default default action name * @throws InvalidConfigException * @throws NotSupportedException * @return string */ - public function getScenarioCommand($default = '') + public function getScenarioAction($default = '') { if ($this->isScenarioDefault()) { if ($default !== '') { @@ -325,17 +329,17 @@ public function getScenarioCommand($default = '') } } else { $scenarioCommands = static::scenarioCommands(); - if ($command = $scenarioCommands[$this->scenario]) { - if ($command === false) { + if ($action = $scenarioCommands[$this->scenario]) { + if ($action === false) { throw new NotSupportedException('The scenario can not be saved'); } - if (is_array($command) && $command[0] === null) { - $result = $command[1]; - } elseif (is_array($command)) { - $result = $command; + if (is_array($action) && $action[0] === null) { + $result = $action[1]; + } elseif (is_array($action)) { + $result = $action; } else { - $result = Inflector::id2camel($command); + $result = Inflector::id2camel($action); } } else { $result = Inflector::id2camel($this->scenario); diff --git a/src/Collection.php b/src/Collection.php index 9df1b16..c7b25d9 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -50,7 +50,6 @@ class Collection extends Component /** * @var string the name of the form. Sets automatically on [[set()]] - * * @see set() */ public $formName; @@ -60,9 +59,7 @@ class Collection extends Component * - model (instance of operating model) * - key - the key of the loaded item * - value - the value of the loaded item - * * Should return array, where the first item is the new key, and the second - a new value. Example: - * * ``` * return [$key, $value]; * ``` @@ -72,7 +69,6 @@ class Collection extends Component /** * @var ActiveRecord the template model instance. May be set manually by [[setModel()]] or * automatically on [[set()]] call - * * @see setModel() * @see set() */ @@ -80,14 +76,12 @@ class Collection extends Component /** * @var array options that will be passed to the new model when loading data in [[load]] - * * @see load() */ public $modelOptions = []; /** * @var ActiveRecord the first model of the set. Fills automatically by [[set()]] - * * @see set() */ public $first; @@ -99,10 +93,8 @@ class Collection extends Component /** * Sets the model of the collection. - * * @param ActiveRecord|array $model if the model is an instance of [[Model]] - sets it, otherwise - creates the model * using given options array - * * @return object|ActiveRecord */ public function setModel($model) @@ -125,7 +117,6 @@ public function setModel($model) /** * Returns the [[model]]. - * * @return ActiveRecord */ public function getModel() @@ -154,7 +145,6 @@ public function getModels() /** * Sets the scenario of the default model. - * * @param $value string scenario */ public function setScenario($value) @@ -164,7 +154,6 @@ public function setScenario($value) /** * Gets the scenario the default model. - * * @return string the scenario */ public function getScenario() @@ -174,7 +163,6 @@ public function getScenario() /** * Updates [[formName]] from the current [[model]]. - * * @return string the form name */ public function updateFormName() @@ -188,14 +176,12 @@ public function updateFormName() /** * We can load data from 3 different structures:. - * * 1) POST: [ * 'ModelName' => [ * 'attribute1' => 'value1', * 'attribute2' => 'value2' * ] * ] - * * 2) POST: [ * 'ModelName' => [ * 1 => [ @@ -214,9 +200,7 @@ public function updateFormName() * If is callable - gets arguments: * - model * - fromName - * * @throws InvalidConfigException - * * @return Collection */ public function load($data = null) @@ -271,9 +255,7 @@ public function load($data = null) /** * Sets the array of AR models to the collection. - * * @param array|ActiveRecord $models - array of AR Models or a single model - * * @return $this */ public function set($models) @@ -282,18 +264,16 @@ public function set($models) $models = [$models]; } - /* @var $first ActiveRecord */ $first = reset($models); if ($first === false) { return $this; } $this->first = $first; - /// redo $this->formName = $first->formName(); $this->model = $first->className(); + $this->models = $models; - $this->models = $models; if ($this->checkConsistency && !$this->isConsistent()) { throw new InvalidValueException('Models are not objects of same class or not follow same operation'); } @@ -303,16 +283,13 @@ public function set($models) /** * Saves the current collection. - * * This method will call [[insert()]] or [[update()]]. - * * @param bool $runValidation whether to perform validation before saving the collection * @param array $attributes list of attribute names that need to be saved. Defaults to null, * meaning all attributes that are loaded will be saved. If the scenario is specified, will use only * fields from the scenario * @param array $options the array of options that will be passed to [[insert]] or [[update]] methods to override * model parameters - * * @return bool whether the saving succeeds */ public function save($runValidation = true, $attributes = null, $options = []) @@ -328,7 +305,7 @@ public function save($runValidation = true, $attributes = null, $options = []) } } - public function insert($runValidation = true, $attributes = null, $options = []) + public function insert($runValidation = true, $attributes = null, array $options = []) { if (!$attributes) { $attributes = $this->attributes ?: $this->first->activeAttributes(); @@ -340,11 +317,11 @@ public function insert($runValidation = true, $attributes = null, $options = []) return false; } - $data = $this->collectData($attributes, $options); - $results = $this->first->perform('create', $data, true); + $options['batch'] = true; + $data = $this->collectData($attributes); + $results = $this->first->perform('create', $data, $options); $pk = $this->first->primaryKey()[0]; foreach ($this->models as $key => $model) { - /* @var $model ActiveRecord */ $values = &$data[$key]; $result = &$results[$key]; @@ -362,7 +339,7 @@ public function insert($runValidation = true, $attributes = null, $options = []) return true; } - public function update($runValidation = true, $attributes = null, $options = []) + public function update($runValidation = true, $attributes = null, array $options = []) { if (!$attributes) { $attributes = $this->attributes ?: $this->first->activeAttributes(); @@ -374,14 +351,14 @@ public function update($runValidation = true, $attributes = null, $options = []) return false; } - $data = $this->collectData($attributes, $options); - $results = $this->first->perform('update', $data, true); + $options['batch'] = true; + $data = $this->collectData($attributes); + $results = $this->first->perform('update', $data, $options); foreach ($this->models as $key => $model) { $changedAttributes = []; $values = array_key_exists($key, $data) ? $data[$key] : $data[$model->id]; /// XXX not good foreach ($values as $name => $value) { - /* @var $model ActiveRecord */ $changedAttributes[$name] = $model->getOldAttribute($name); $model->setOldAttribute($name, $value); } @@ -409,28 +386,15 @@ public function delete() /** * Collects data from the stored models. - * - * @param string|array $attributes list of attributes names - * @param callable|array $options overrides the model attributes - * If is array - merges with the model attributes - * If is callable - gets two arguments: - * 1) the array of model attributes - * 2) the model object - * + * @param string|array $attributes list of attributes names * @return array */ - public function collectData($attributes = null, $options = []) + public function collectData($attributes = null) { - $data = []; + $data = []; foreach ($this->models as $model) { - /* @var $model ActiveRecord */ $key = $model->getPrimaryKey(); - if ($options instanceof Closure) { - $row = call_user_func($options, $model->getAttributes($attributes), $model); - } else { - $row = array_merge($model->getAttributes($attributes), $options); - } - + $row = $model->getAttributes($attributes); if ($key) { $data[$key] = $row; } else { @@ -443,13 +407,11 @@ public function collectData($attributes = null, $options = []) /** * Whether one of models has an error. - * * @return bool */ public function hasErrors() { foreach ($this->models as $model) { - /* @var $model ActiveRecord */ if ($model->hasErrors()) { return true; } @@ -460,13 +422,11 @@ public function hasErrors() /** * Returns the first error of the collection. - * * @return bool|mixed */ public function getFirstError() { foreach ($this->models as $model) { - /* @var $model ActiveRecord */ if ($model->hasErrors()) { $errors = $model->getFirstErrors(); @@ -558,10 +518,8 @@ public function afterDelete() /** * Iterates over all of the models and triggers some event. - * * @param string $name the event name * @param ModelEvent $event - * * @return bool whether is valid */ public function triggerModels($name, ModelEvent $event = null) @@ -570,7 +528,6 @@ public function triggerModels($name, ModelEvent $event = null) $event = new ModelEvent(); } foreach ($this->models as $model) { - /* @var $model ActiveRecord */ $model->trigger($name, $event); } @@ -579,10 +536,8 @@ public function triggerModels($name, ModelEvent $event = null) /** * Calls [[triggerModels()]], then calls [[trigger()]]. - * * @param string $name the event name * @param ModelEvent $event - * * @return bool whether is valid */ public function triggerAll($name, ModelEvent $event = null) @@ -602,7 +557,6 @@ public function isConsistent() $new = $this->first->getIsNewRecord(); $className = $this->first->className(); foreach ($this->models as $model) { - /* @var $model ActiveRecord */ if ($new !== $model->getIsNewRecord() || $className !== $model->className()) { return false; } diff --git a/src/Command.php b/src/Command.php index 4b78367..7fe418c 100644 --- a/src/Command.php +++ b/src/Command.php @@ -83,7 +83,9 @@ public function delete($table, $condition, array $options = []) /** * Creates and executes request with given data. * @param string $action - * @param mixed $body request parameters + * @param string $table + * @param mixed $body + * @param array $options * @return mixed */ public function perform($action, $table, $body = [], array $options = []) diff --git a/src/Connection.php b/src/Connection.php index 88d2cc5..c9d3c8f 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -39,6 +39,8 @@ class Connection extends Component public $commandClass = Command::class; + public $queryBuilderClass = QueryBuilder::class; + public $queryClass = Query::class; public $activeQueryClass = ActiveQuery::class; @@ -146,6 +148,19 @@ public function getDriverName() return 'hiart'; } + /** + * Performs command. + * @param string $action + * @param string $table + * @param mixed $body + * @param array $options + * @return mixed + */ + public function perform($action, $table, $body = [], array $options = []) + { + return $this->createCommand()->perform($action, $table, $body, $options); + } + /** * Creates a command for execution. * @param array $config the configuration for the Command class @@ -176,7 +191,7 @@ public function getQueryBuilder() */ public function createQueryBuilder() { - return new QueryBuilder($this); + return new $this->queryBuilderClass($this); } From 632fe979f16256a3a5e7a13e88e17a19b6b01d73 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Fri, 20 Jan 2017 17:02:05 +0000 Subject: [PATCH 17/53] removed direct HTTP requesting functions: get, head, post, put, delete --- src/Connection.php | 74 ---------------------------------------------- 1 file changed, 74 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index c9d3c8f..f676cd4 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -195,80 +195,6 @@ public function createQueryBuilder() } - /** - * Performs GET HTTP request. - * @param string $url URL - * @param array $query query options (GET parameters) - * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` - * @throws HiArtException - * @throws \yii\base\InvalidConfigException - * @return mixed response - */ - public function get($url, $query = [], $raw = false) - { - return $this->makeRequest('GET', $url, $query, null, $raw); - } - - /** - * Performs HEAD HTTP request. - * @param string $url URL - * @param array $query query options - * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` - * @throws HiArtException - * @throws \yii\base\InvalidConfigException - * @return mixed response - */ - public function head($url, $query = [], $body = null) - { - return $this->makeRequest('HEAD', $url, $query, $body); - } - - /** - * Performs POST HTTP request. - * @param string $url URL - * @param array $query query options (GET parameters) - * @param string $body request body (POST parameters) - * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` - * @throws HiArtException - * @throws \yii\base\InvalidConfigException - * @return mixed response - */ - public function post($url, $query = [], $body = null, $raw = false) - { - return $this->makeRequest('POST', $url, $query, $body, $raw); - } - - /** - * Performs PUT HTTP request. - * @param string $url URL - * @param array $query query options (GET parameters) - * @param string $body request body (POST parameters) - * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` - * @throws HiArtException - * @throws \yii\base\InvalidConfigException - * @return mixed response - */ - public function put($url, $query = [], $body = null, $raw = false) - { - return $this->makeRequest('PUT', $url, $query, $body, $raw); - } - - /** - * Performs DELETE HTTP request. - * @param string $url URL - * @param array $query query options (GET parameters) - * @param string $body request body (POST parameters) - * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` - * @throws HiArtException - * @throws \yii\base\InvalidConfigException - * @return mixed response - */ - public function delete($url, $query = [], $body = null, $raw = false) - { - return $this->makeRequest('DELETE', $url, $query, $body, $raw); - } - - /** * Make request and check for error. * @param string $url URL From eea45753ca0c762c1f5cf4a3ee745f4996267e5a Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Fri, 20 Jan 2017 17:02:57 +0000 Subject: [PATCH 18/53] removed direct perform() from Connection --- src/Connection.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/Connection.php b/src/Connection.php index f676cd4..ca910f0 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -148,19 +148,6 @@ public function getDriverName() return 'hiart'; } - /** - * Performs command. - * @param string $action - * @param string $table - * @param mixed $body - * @param array $options - * @return mixed - */ - public function perform($action, $table, $body = [], array $options = []) - { - return $this->createCommand()->perform($action, $table, $body, $options); - } - /** * Creates a command for execution. * @param array $config the configuration for the Command class From 6cbe9cbe142d14149424a285775e46931a584a03 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Fri, 20 Jan 2017 19:58:09 +0000 Subject: [PATCH 19/53] fixed error handling --- src/Command.php | 8 ++-- src/Connection.php | 104 +++++++++++---------------------------------- src/Request.php | 42 ++++++++++++------ 3 files changed, 58 insertions(+), 96 deletions(-) diff --git a/src/Command.php b/src/Command.php index 7fe418c..51e7932 100644 --- a/src/Command.php +++ b/src/Command.php @@ -38,7 +38,7 @@ public function setRequest(Request $request) * Sends a request to retrieve data. * In API this could be get, search or list request. * @throws ErrorResponseException - * @return mixed + * @return mixed response data */ public function search() { @@ -86,7 +86,7 @@ public function delete($table, $condition, array $options = []) * @param string $table * @param mixed $body * @param array $options - * @return mixed + * @return mixed response data */ public function perform($action, $table, $body = [], array $options = []) { @@ -100,7 +100,7 @@ public function perform($action, $table, $body = [], array $options = []) * Executes the request. * @param string $url URL * @param mixed $body request parameters - * @return mixed + * @return mixed response data */ public function execute() { @@ -110,7 +110,7 @@ public function execute() $response = $this->db->send($this->request); Yii::endProfile($profile, $category); - return $response->getData(); + return $this->db->checkResponse($response); } public static function getProfileCategory() diff --git a/src/Connection.php b/src/Connection.php index ca910f0..ffc5a6f 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -182,67 +182,6 @@ public function createQueryBuilder() } - /** - * Make request and check for error. - * @param string $url URL - * @param array $query query options (GET parameters) - * @param string $body request body (POST parameters) - * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` - * @throws HiArtException - * @throws \yii\base\InvalidConfigException - * @return mixed response - */ - public function makeRequest($method, $url, $query = [], $body = null, $raw = false) - { - $result = $this->handleRequest($method, $this->prepareUrl($url, $query), $body, $raw); - - return $this->checkResponse($result, $url, $query); - } - - /** - * Creates URL by joining path part and query options. - * @param mixed $path path - * @param array $query query options - * @return array - */ - private function prepareUrl($path, array $query = []) - { - $url = $path; - $query = array_merge($this->getAuth(), $query); - if (!empty($query)) { - $url .= (strpos($url, '?') === false ? '?' : '&') . http_build_query($query); - } - - return $url; - } - - /** - * Handles the request with handler. - * Returns array or raw response content, if $raw is true. - * - * @param string $method POST, GET, etc - * @param string $url the URL for request, not including proto and site - * @param array|string $body the request body. When array - will be sent as POST params, otherwise - as RAW body. - * @param bool $raw Do not try to decode data, event when response is decodeable (JSON). Defaults to `false` - * @return array|string - */ - protected function handleRequest($method, $url, $body = null, $raw = false) - { - $method = strtoupper($method); - $profile = $method . ' ' . $url . '#' . (is_array($body) ? http_build_query($body) : $body); - $options = [(is_array($body) ? 'form_params' : 'body') => $body]; - Yii::beginProfile($profile, __METHOD__); - $response = $this->getHandler()->request($method, $url, $options); - Yii::endProfile($profile, __METHOD__); - - $res = $response->getBody()->getContents(); - if (!$raw && preg_grep('|application/json|i', $response->getHeader('Content-Type'))) { - $res = Json::decode($res); - } - - return $res; - } - /** * Returns the request handler (Guzzle client for the moment). * Creates and setups handler if not set. @@ -317,46 +256,53 @@ protected function decodeErrorBody($body) /** * Setter for errorChecker. - * @param Closure|array $value + * @param Closure $checker */ - public function setErrorChecker($value) + public function setErrorChecker($checker) { - $this->_errorChecker = $value; + $this->_errorChecker = $checker; } /** * Checks response with checkError method and raises exception if error. - * @param array $response response data from API - * @param string $url request URL - * @param array $options request data + * @param Response $response response data from API * @throws ErrorResponseException - * @return array + * @return mixed response data */ - protected function checkResponse($response, $url, $options) + public function checkResponse(Response $response) { $error = $this->checkError($response); - if (isset($error)) { + if ($error) { throw new ErrorResponseException($error, [ - 'requestUrl' => $url, - 'request' => $options, - 'response' => $response, + 'request' => $response->getRequest()->getParts(), + 'response' => $response->getData(), ]); } - return $response; + return $response->getData(); } /** - * Checks response with errorChecker callback and returns not null if error. - * @param array $response response data from API - * @return null|string + * Checks response with errorChecker callback and returns error text if error. + * @param Response $response + * @return string|false error text or false */ - public function checkError($response) + public function checkError(Response $response) { if (isset($this->_errorChecker)) { return call_user_func($this->_errorChecker, $response); + } else { + return $this->isError($response); } + } - return null; + /** + * Default error checker. TODO check something in response? + * @param Response $response + * @return bool + */ + public function isError(Response $response) + { + return false; } } diff --git a/src/Request.php b/src/Request.php index 8955c2f..097907c 100644 --- a/src/Request.php +++ b/src/Request.php @@ -21,6 +21,8 @@ class Request implements \Serializable */ protected $worker; + protected $workerClass = Worker::class; + /** * @var Query */ @@ -40,6 +42,8 @@ class Request implements \Serializable protected $body; protected $version; + protected $parts = []; + public function __construct(QueryBuilder $builder, Query $query) { $this->builder = $builder; @@ -76,6 +80,17 @@ public function getVersion() return $this->version; } + /** + * @return Query + */ + public function getQuery() + { + return $this->query; + } + + /** + * @return Worker + */ public function getWorker() { if ($this->worker === null) { @@ -88,11 +103,6 @@ public function getWorker() return $this->worker; } - public function getQuery() - { - return $this->query; - } - protected function updateFromQuery() { $this->builder->prepare($this->query); @@ -110,7 +120,7 @@ protected function updateFromQuery() protected function createWorker() { - return new Worker($this->method, $this->uri, $this->headers, $this->body, $this->version); + return new $this->workerClass($this->method, $this->uri, $this->headers, $this->body, $this->version); } protected function buildDbname() @@ -174,13 +184,7 @@ protected function buildProtocolVersion() public function serialize() { - $this->getWorker(); - $data = []; - foreach (['dbname', 'method', 'uri', 'headers', 'body', 'version'] as $key) { - $data[$key] = $this->{$key}; - } - - return serialize($data); + return serialize($this->getParts()); } public function unserialize($string) @@ -190,6 +194,18 @@ public function unserialize($string) } } + public function getParts() + { + if (empty($this->parts)) { + $this->getWorker(); + foreach (['dbname', 'method', 'uri', 'headers', 'body', 'version'] as $key) { + $this->parts[$key] = $this->{$key}; + } + } + + return $this->parts; + } + public function isRaw() { return !empty($this->query->options['raw']); From 414d5ad978111d641e0be4ae8d6b956fe835e485 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sat, 21 Jan 2017 18:14:02 +0200 Subject: [PATCH 20/53] docs --- LICENSE | 2 +- README.md | 71 +++++++++++++++++++++++++++++++----- docs/readme/Configuration.md | 41 +++++++++++++++++++-- docs/readme/Usage.md | 27 ++++++++++++-- 4 files changed, 123 insertions(+), 18 deletions(-) diff --git a/LICENSE b/LICENSE index 8592466..021fe68 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright © 2015-2016, HiQDev (http://hiqdev.com/) +Copyright © 2015-2017, HiQDev (http://hiqdev.com/) All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/README.md b/README.md index cf4e020..4627981 100644 --- a/README.md +++ b/README.md @@ -37,34 +37,87 @@ To use this extension, configure hiart component in your application config: ```php 'components' => [ 'hiart' => [ - 'class' => 'hiqdev\hiart\Connection', - 'config' => [ - 'base_uri' => 'https://api.site.com/', - ], + 'class' => \hiqdev\hiart\curl\Connection::class, + 'queryBuilderClass' => \hiqdev\hiart\rest\QueryBuilder::class, + 'baseUri' => 'https://site.com/api/v3/', ], ], ``` +Note three main options: + +- `class` specifies transport implementation to be used, **cURL** in this case +- `queryBuilderClass` specifies class that actually implements API to be accessed, **REST** in this case +- `baseUri` specifies starting point of the API + +Available transports are: + +- [cURL](http://php.net/manual/en/book.curl.php) +- [PHP streams](http://php.net/manual/en/book.stream.php) +- [Guzzle](https://github.com/guzzle/guzzle), provided with [yii2-hiart-guzzle](https://github.com/hiqdev/yii2-hiart-guzzle) +- [yii2-httpclient](https://github.com/yiisoft/yii2-httpclient), provided with [yii2-hiart-httpclient](https://github.com/hiqdev/yii2-hiart-httpclient) + +You can implement your own transport, it's not difficult see available implementations. +It can be even not HTTP based. + +There are `QueryBuilder`s for: + +- Basic [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) +- [GitHub API](https://developer.github.com/v3/), provided with [yii2-hiart-github](https://github.com/hiqdev/yii2-hiart-github) +- [HiPanel](https://hipanel.com) API, provided with [hipanel-hiart](https://github.com/hiqdev/hipanel-hiart) + +You can implement your own API. +Basically all you need is create your QueryBuilder with these methods: + +- `buildMethod(Query $query)` +- `buildHeaders(Query $query)` +- `buildUri(Query $query)` +- `buildQueryParams(Query $query)` +- `buildBody(Query $query)` +- `buildFormParams(Query $query)` + +See available implementations and create issues on GitHub. + ## Usage -Define your Model +Define your Model: ```php -class MyModel extends \hiqdev\hiart\ActiveRecord +class User extends \hiqdev\hiart\ActiveRecord { - public function attributes() + public function rules() { - return ['id', 'name', 'else']; + return [ + ['id', 'integer', 'min' => 1], + ['login', 'string', 'min' => 2, 'max' => 32], + ]; } } ``` +Note that you use general `hiqdev\hiart\ActiveRecord` class not specific for certain API. +API is specified in connection options and you don't need to change model classes when +you change API. + +Then you just use your models same way as DB ActiveRecord models. + +```php +$user = new User(); +$user->login = 'sol'; + +$user->save(); + +$admins = User::find()->where(['type' => User::ADMIN_TYPE])->all(); +``` + +Basically all features of Yii ActiveRecords work if your API provides them. + ## License This project is released under the terms of the BSD-3-Clause [license](LICENSE). Read more [here](http://choosealicense.com/licenses/bsd-3-clause). -Copyright © 2015-2016, HiQDev (http://hiqdev.com/) +Copyright © 2015-2017, HiQDev (http://hiqdev.com/) ## Acknowledgments diff --git a/docs/readme/Configuration.md b/docs/readme/Configuration.md index 2d31b82..02cd9e3 100644 --- a/docs/readme/Configuration.md +++ b/docs/readme/Configuration.md @@ -3,10 +3,43 @@ To use this extension, configure hiart component in your application config: ```php 'components' => [ 'hiart' => [ - 'class' => 'hiqdev\hiart\Connection', - 'config' => [ - 'base_uri' => 'https://api.site.com/', - ], + 'class' => \hiqdev\hiart\curl\Connection::class, + 'queryBuilderClass' => \hiqdev\hiart\rest\QueryBuilder::class, + 'baseUri' => 'https://site.com/api/v3/', ], ], ``` + +Note three main options: + +- `class` specifies transport implementation to be used, **cURL** in this case +- `queryBuilderClass` specifies class that actually implements API to be accessed, **REST** in this case +- `baseUri` specifies starting point of the API + +Available transports are: + +- [cURL](http://php.net/manual/en/book.curl.php) +- [PHP streams](http://php.net/manual/en/book.stream.php) +- [Guzzle](https://github.com/guzzle/guzzle), provided with [yii2-hiart-guzzle](https://github.com/hiqdev/yii2-hiart-guzzle) +- [yii2-httpclient](https://github.com/yiisoft/yii2-httpclient), provided with [yii2-hiart-httpclient](https://github.com/hiqdev/yii2-hiart-httpclient) + +You can implement your own transport, it's not difficult see available implementations. +It can be even not HTTP based. + +There are `QueryBuilder`s for: + +- Basic [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) +- [GitHub API](https://developer.github.com/v3/), provided with [yii2-hiart-github](https://github.com/hiqdev/yii2-hiart-github) +- [HiPanel](https://hipanel.com) API, provided with [hipanel-hiart](https://github.com/hiqdev/hipanel-hiart) + +You can implement your own API. +Basically all you need is create your QueryBuilder with these methods: + +- `buildMethod(Query $query)` +- `buildHeaders(Query $query)` +- `buildUri(Query $query)` +- `buildQueryParams(Query $query)` +- `buildBody(Query $query)` +- `buildFormParams(Query $query)` + +See available implementations and create issues on GitHub. diff --git a/docs/readme/Usage.md b/docs/readme/Usage.md index 2d54c1e..7014a84 100644 --- a/docs/readme/Usage.md +++ b/docs/readme/Usage.md @@ -1,12 +1,31 @@ -Define your Model +Define your Model: ```php -class MyModel extends \hiqdev\hiart\ActiveRecord +class User extends \hiqdev\hiart\ActiveRecord { - public function attributes() + public function rules() { - return ['id', 'name', 'else']; + return [ + ['id', 'integer', 'min' => 1], + ['login', 'string', 'min' => 2, 'max' => 32], + ]; } } ``` +Note that you use general `hiqdev\hiart\ActiveRecord` class not specific for certain API. +API is specified in connection options and you don't need to change model classes when +you change API. + +Then you just use your models same way as DB ActiveRecord models. + +```php +$user = new User(); +$user->login = 'sol'; + +$user->save(); + +$admins = User::find()->where(['type' => User::ADMIN_TYPE])->all(); +``` + +Basically all features of Yii ActiveRecords work if your API provides them. From 930b738d9fcb4dfff41eee349e338220eb43b6c2 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sat, 21 Jan 2017 18:42:22 +0200 Subject: [PATCH 21/53] renamed Exception <- HiArtException --- src/ErrorResponseException.php | 5 ++--- src/Exception.php | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/Exception.php diff --git a/src/ErrorResponseException.php b/src/ErrorResponseException.php index 0f4f84d..c5bcb48 100644 --- a/src/ErrorResponseException.php +++ b/src/ErrorResponseException.php @@ -11,10 +11,9 @@ namespace hiqdev\hiart; /** - * Class ErrorResponseException - * Exception. + * Class ErrorResponseException. */ -class ErrorResponseException extends HiArtException +class ErrorResponseException extends Exception { /** * @var array The API response diff --git a/src/Exception.php b/src/Exception.php new file mode 100644 index 0000000..47ed54c --- /dev/null +++ b/src/Exception.php @@ -0,0 +1,22 @@ + Date: Sat, 21 Jan 2017 19:31:01 +0200 Subject: [PATCH 22/53] docs --- README.md | 19 ++++++++++--------- docs/readme/Configuration.md | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 4627981..380115c 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,8 @@ To use this extension, configure hiart component in your application config: ```php 'components' => [ 'hiart' => [ - 'class' => \hiqdev\hiart\curl\Connection::class, - 'queryBuilderClass' => \hiqdev\hiart\rest\QueryBuilder::class, + 'class' => \hiqdev\hiart\rest\Connection::class, + 'requestClass' => \hiqdev\hiart\curl\Request::class, 'baseUri' => 'https://site.com/api/v3/', ], ], @@ -46,28 +46,29 @@ To use this extension, configure hiart component in your application config: Note three main options: -- `class` specifies transport implementation to be used, **cURL** in this case -- `queryBuilderClass` specifies class that actually implements API to be accessed, **REST** in this case +- `class` specifies class that actually implements API to be accessed, **REST** in this case +- `requestClass` specifies transport implementation to be used, **cURL** in this case - `baseUri` specifies starting point of the API Available transports are: -- [cURL](http://php.net/manual/en/book.curl.php) -- [PHP streams](http://php.net/manual/en/book.stream.php) +- [PHP streams](http://php.net/manual/en/book.stream.php), **default**, included in this package +- [cURL](http://php.net/manual/en/book.curl.php), included in this package - [Guzzle](https://github.com/guzzle/guzzle), provided with [yii2-hiart-guzzle](https://github.com/hiqdev/yii2-hiart-guzzle) - [yii2-httpclient](https://github.com/yiisoft/yii2-httpclient), provided with [yii2-hiart-httpclient](https://github.com/hiqdev/yii2-hiart-httpclient) You can implement your own transport, it's not difficult see available implementations. +All you need is to create two classes: It can be even not HTTP based. There are `QueryBuilder`s for: -- Basic [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) +- Basic [REST](https://en.wikipedia.org/wiki/Representational_state_transfer), included in this package - [GitHub API](https://developer.github.com/v3/), provided with [yii2-hiart-github](https://github.com/hiqdev/yii2-hiart-github) -- [HiPanel](https://hipanel.com) API, provided with [hipanel-hiart](https://github.com/hiqdev/hipanel-hiart) +- [HiPanel API](https://hipanel.com/), provided with [hipanel-hiart](https://github.com/hiqdev/hipanel-hiart) You can implement your own API. -Basically all you need is create your QueryBuilder with these methods: +Basically all you need is to create your QueryBuilder with these methods: - `buildMethod(Query $query)` - `buildHeaders(Query $query)` diff --git a/docs/readme/Configuration.md b/docs/readme/Configuration.md index 02cd9e3..d605c4b 100644 --- a/docs/readme/Configuration.md +++ b/docs/readme/Configuration.md @@ -3,8 +3,8 @@ To use this extension, configure hiart component in your application config: ```php 'components' => [ 'hiart' => [ - 'class' => \hiqdev\hiart\curl\Connection::class, - 'queryBuilderClass' => \hiqdev\hiart\rest\QueryBuilder::class, + 'class' => \hiqdev\hiart\rest\Connection::class, + 'requestClass' => \hiqdev\hiart\curl\Request::class, 'baseUri' => 'https://site.com/api/v3/', ], ], @@ -12,28 +12,29 @@ To use this extension, configure hiart component in your application config: Note three main options: -- `class` specifies transport implementation to be used, **cURL** in this case -- `queryBuilderClass` specifies class that actually implements API to be accessed, **REST** in this case +- `class` specifies class that actually implements API to be accessed, **REST** in this case +- `requestClass` specifies transport implementation to be used, **cURL** in this case - `baseUri` specifies starting point of the API Available transports are: -- [cURL](http://php.net/manual/en/book.curl.php) -- [PHP streams](http://php.net/manual/en/book.stream.php) +- [PHP streams](http://php.net/manual/en/book.stream.php), **default**, included in this package +- [cURL](http://php.net/manual/en/book.curl.php), included in this package - [Guzzle](https://github.com/guzzle/guzzle), provided with [yii2-hiart-guzzle](https://github.com/hiqdev/yii2-hiart-guzzle) - [yii2-httpclient](https://github.com/yiisoft/yii2-httpclient), provided with [yii2-hiart-httpclient](https://github.com/hiqdev/yii2-hiart-httpclient) You can implement your own transport, it's not difficult see available implementations. +All you need is to create two classes: It can be even not HTTP based. There are `QueryBuilder`s for: -- Basic [REST](https://en.wikipedia.org/wiki/Representational_state_transfer) +- Basic [REST](https://en.wikipedia.org/wiki/Representational_state_transfer), included in this package - [GitHub API](https://developer.github.com/v3/), provided with [yii2-hiart-github](https://github.com/hiqdev/yii2-hiart-github) -- [HiPanel](https://hipanel.com) API, provided with [hipanel-hiart](https://github.com/hiqdev/hipanel-hiart) +- [HiPanel API](https://hipanel.com/), provided with [hipanel-hiart](https://github.com/hiqdev/hipanel-hiart) You can implement your own API. -Basically all you need is create your QueryBuilder with these methods: +Basically all you need is to create your QueryBuilder with these methods: - `buildMethod(Query $query)` - `buildHeaders(Query $query)` From 35293a6f0e5e5cb06192c83c9c4119b88a83e7a6 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sat, 21 Jan 2017 20:01:17 +0200 Subject: [PATCH 23/53] inited rest --- src/rest/Connection.php | 8 ++++++++ src/rest/QueryBuilder.php | 10 ++++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/rest/Connection.php create mode 100644 src/rest/QueryBuilder.php diff --git a/src/rest/Connection.php b/src/rest/Connection.php new file mode 100644 index 0000000..90f9816 --- /dev/null +++ b/src/rest/Connection.php @@ -0,0 +1,8 @@ + Date: Sat, 21 Jan 2017 20:03:03 +0200 Subject: [PATCH 24/53] inited curl --- src/curl/Request.php | 19 +++++++++++++++++++ src/curl/Response.php | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/curl/Request.php create mode 100644 src/curl/Response.php diff --git a/src/curl/Request.php b/src/curl/Request.php new file mode 100644 index 0000000..ea58e20 --- /dev/null +++ b/src/curl/Request.php @@ -0,0 +1,19 @@ + + */ +class Request extends AbstractRequest +{ + protected $workerClass = RequestWorker::class; + + protected function createWorker() + { + } +} diff --git a/src/curl/Response.php b/src/curl/Response.php new file mode 100644 index 0000000..597a41e --- /dev/null +++ b/src/curl/Response.php @@ -0,0 +1,18 @@ + + */ +class Response extends AbstractResponse +{ + /** + * @var ResponseWorker + */ + protected $worker; +} From fcd5feb30f7bfe60d97569db6e4567f3acd68139 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sat, 21 Jan 2017 21:01:45 +0200 Subject: [PATCH 25/53] redone to abstract classes --- composer.json | 6 +- ...{Connection.php => AbstractConnection.php} | 65 +++++++------------ ...ryBuilder.php => AbstractQueryBuilder.php} | 56 ++++------------ src/{Request.php => AbstractRequest.php} | 31 ++++++--- src/{Response.php => AbstractResponse.php} | 0 src/HiArtException.php | 22 ------- src/rest/QueryBuilder.php | 53 ++++++++++++++- 7 files changed, 112 insertions(+), 121 deletions(-) rename src/{Connection.php => AbstractConnection.php} (83%) rename src/{QueryBuilder.php => AbstractQueryBuilder.php} (86%) rename src/{Request.php => AbstractRequest.php} (86%) rename src/{Response.php => AbstractResponse.php} (100%) delete mode 100644 src/HiArtException.php diff --git a/composer.json b/composer.json index 960b081..1bbb40f 100644 --- a/composer.json +++ b/composer.json @@ -27,19 +27,19 @@ { "name": "Dmitry Naumenko", "role": "Lead backend developer", - "email": "silverfire@hiqdev.com", + "email": "d.naumenko.a@gmail.com", "homepage": "http://silverfire.me/" }, { "name": "Andrey Klochok", "role": "Lead frontend developer", - "email": "tafid@hiqdev.com", + "email": "andreyklochok@gmail.com", "homepage": "http://hiqdev.com/" }, { "name": "Yuriy Myronchuk", "role": "QA Lead", - "email": "bladeroot@hiqdev.com", + "email": "bladeroot@gmail.com", "homepage": "http://hiqdev.com/" } ], diff --git a/src/Connection.php b/src/AbstractConnection.php similarity index 83% rename from src/Connection.php rename to src/AbstractConnection.php index ffc5a6f..41c5a7d 100644 --- a/src/Connection.php +++ b/src/AbstractConnection.php @@ -11,35 +11,28 @@ namespace hiqdev\hiart; use Closure; -use GuzzleHttp\Client as Handler; -use Yii; +use hiqdev\hiart\stream\Request; use yii\base\Component; use yii\base\InvalidConfigException; use yii\base\InvalidParamException; use yii\helpers\Json; /** - * Connection class. + * Abstract connection class. * - * Example configuration: - * ```php - * 'components' => [ - * 'hiart' => [ - * 'class' => 'hiqdev\hiart\Connection', - * 'config' => [ - * 'base_uri' => 'https://api.site.com/', - * ], - * ], - * ], - * ``` */ -class Connection extends Component +abstract class AbstractConnection extends Component { const EVENT_AFTER_OPEN = 'afterOpen'; - public $commandClass = Command::class; + /** + * @var string to be specified in concrete implementation. + */ + public $queryBuilderClass; - public $queryBuilderClass = QueryBuilder::class; + public $requestClass = Request::class; + + public $commandClass = Command::class; public $queryClass = Query::class; @@ -48,14 +41,14 @@ class Connection extends Component public $name = 'hiart'; /** - * @var array Config + * @var array connection config will be passed to handler */ public $config = []; /** - * @var Handler request handler + * @var object request handler common for all requests of this connection. */ - protected static $_handler; + protected $_handler; /** * @var QueryBuilder the query builder for this connection @@ -166,43 +159,29 @@ public function createCommand(array $config = []) public function getQueryBuilder() { if ($this->_builder === null) { - $this->_builder = $this->createQueryBuilder(); + $this->_builder = new $this->queryBuilderClass($this); } return $this->_builder; } /** - * Creates new query builder instance. - * @return QueryBuilder - */ - public function createQueryBuilder() - { - return new $this->queryBuilderClass($this); - } - - - /** - * Returns the request handler (Guzzle client for the moment). - * Creates and setups handler if not set. - * @return Handler + * Handler is created and set by request. + * @see setHandler + * @return object */ public function getHandler() { - if (static::$_handler === null) { - static::$_handler = new Handler($this->config); - } - - return static::$_handler; + return $this->_handler; } /** - * Set handler manually. - * @param Handler $value + * Requests use this function to keep request handler. + * @param object $handler */ - public function setHandler(Handler $value) + public function setHandler($handler) { - static::$_handler = $value; + $this->_handler = $handler; } /** diff --git a/src/QueryBuilder.php b/src/AbstractQueryBuilder.php similarity index 86% rename from src/QueryBuilder.php rename to src/AbstractQueryBuilder.php index 996f1b8..bca1325 100644 --- a/src/QueryBuilder.php +++ b/src/AbstractQueryBuilder.php @@ -15,9 +15,11 @@ use yii\helpers\ArrayHelper; /** - * QueryBuilder builds a PSR7 request from the specification given as a [[Query]] object. + * Abstract QueryBuilder. + * + * QueryBuilder builds a request from the specification given as a [[Query]] object. */ -class QueryBuilder extends \yii\base\Object +abstract class AbstractQueryBuilder extends \yii\base\Object { public $db; @@ -58,55 +60,21 @@ public function prepare(Query $query) * This function is for you to provide your authentication. * @param Query $query */ - public function buildAuth(Query $query) - { - } + abstract public function buildAuth(Query $query); - public function buildMethod(Query $query) - { - static $defaultMethods = [ - 'get' => 'GET', - 'put' => 'PUT', - 'head' => 'HEAD', - 'post' => 'GET', - 'search' => 'GET', - 'insert' => 'POST', - 'update' => 'PUT', - 'delete' => 'DELETE', - ]; + abstract public function buildMethod(Query $query); - return isset($defaultMethods[$query->action]) ? $defaultMethods[$query->action] : 'POST'; - } + abstract public function buildUri(Query $query); - public function buildUri(Query $query) - { - return $query->from; - } + abstract public function buildHeaders(Query $query); - public function buildHeaders(Query $query) - { - return []; - } + abstract public function buildProtocolVersion(Query $query); - public function buildProtocolVersion(Query $query) - { - return null; - } + abstract public function buildQueryParams(Query $query); - public function buildQueryParams(Query $query) - { - return []; - } + abstract public function buildFormParams(Query $query); - public function buildFormParams(Query $query) - { - return []; - } - - public function buildBody(Query $query) - { - return null; - } + abstract public function buildBody(Query $query); /** * Creates insert request. diff --git a/src/Request.php b/src/AbstractRequest.php similarity index 86% rename from src/Request.php rename to src/AbstractRequest.php index 097907c..1c2a266 100644 --- a/src/Request.php +++ b/src/AbstractRequest.php @@ -10,19 +10,25 @@ namespace hiqdev\hiart; -use GuzzleHttp\Psr7\Request as Worker; - -class Request implements \Serializable +abstract class AbstractRequest implements \Serializable { + /** + * @var string response implementation to be specified in concrete implementation. + */ + protected $responseClass; + + /** + * @var string transport implementation to be specified in concrete implementation. + */ + public $handlerClass; + protected $builder; /** - * @var Worker + * @var object */ protected $worker; - protected $workerClass = Worker::class; - /** * @var Query */ @@ -118,9 +124,14 @@ protected function updateFromQuery() $this->buildProtocolVersion(); } - protected function createWorker() + abstract protected function createWorker(); + + public function send($options = []) { - return new $this->workerClass($this->method, $this->uri, $this->headers, $this->body, $this->version); + $handler = $this->builder->getHandler(); + $worker = $handler->send($this->getWorker(), $options); + + return new $this->responseClass($worker, $request); } protected function buildDbname() @@ -206,6 +217,10 @@ public function getParts() return $this->parts; } + public function send() + { + } + public function isRaw() { return !empty($this->query->options['raw']); diff --git a/src/Response.php b/src/AbstractResponse.php similarity index 100% rename from src/Response.php rename to src/AbstractResponse.php diff --git a/src/HiArtException.php b/src/HiArtException.php deleted file mode 100644 index 4860839..0000000 --- a/src/HiArtException.php +++ /dev/null @@ -1,22 +0,0 @@ - 'GET', + 'put' => 'PUT', + 'head' => 'HEAD', + 'post' => 'GET', + 'search' => 'GET', + 'insert' => 'POST', + 'update' => 'PUT', + 'delete' => 'DELETE', + ]; + + return isset($defaultMethods[$query->action]) ? $defaultMethods[$query->action] : 'POST'; + } + + public function buildUri(Query $query) + { + return $query->from; + } + + public function buildHeaders(Query $query) + { + return []; + } + + public function buildProtocolVersion(Query $query) + { + return null; + } + + public function buildQueryParams(Query $query) + { + return []; + } + + public function buildFormParams(Query $query) + { + return []; + } + + public function buildBody(Query $query) + { + return null; + } + } From c4c4eafd44a527648ddd683dc8e9067e495fc999 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 08:10:01 +0200 Subject: [PATCH 26/53] + QueryBuilderInterface --- src/AbstractQueryBuilder.php | 2 +- src/QueryBuilderInterface.php | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/QueryBuilderInterface.php diff --git a/src/AbstractQueryBuilder.php b/src/AbstractQueryBuilder.php index bca1325..7f2f795 100644 --- a/src/AbstractQueryBuilder.php +++ b/src/AbstractQueryBuilder.php @@ -19,7 +19,7 @@ * * QueryBuilder builds a request from the specification given as a [[Query]] object. */ -abstract class AbstractQueryBuilder extends \yii\base\Object +abstract class AbstractQueryBuilder extends \yii\base\Object implements QueryBuilderInterface { public $db; diff --git a/src/QueryBuilderInterface.php b/src/QueryBuilderInterface.php new file mode 100644 index 0000000..f51fe10 --- /dev/null +++ b/src/QueryBuilderInterface.php @@ -0,0 +1,44 @@ + Date: Sun, 22 Jan 2017 08:11:48 +0200 Subject: [PATCH 27/53] separated out ProxyRequest --- src/AbstractRequest.php | 64 +++++++++++++++++++++-------------------- src/ProxyRequest.php | 47 ++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 31 deletions(-) create mode 100644 src/ProxyRequest.php diff --git a/src/AbstractRequest.php b/src/AbstractRequest.php index 1c2a266..57cc4ae 100644 --- a/src/AbstractRequest.php +++ b/src/AbstractRequest.php @@ -18,17 +18,10 @@ abstract class AbstractRequest implements \Serializable protected $responseClass; /** - * @var string transport implementation to be specified in concrete implementation. + * @var QueryBuilderInterface */ - public $handlerClass; - protected $builder; - /** - * @var object - */ - protected $worker; - /** * @var Query */ @@ -50,7 +43,9 @@ abstract class AbstractRequest implements \Serializable protected $parts = []; - public function __construct(QueryBuilder $builder, Query $query) + abstract public function send($options = []); + + public function __construct(QueryBuilderInterface $builder, Query $query) { $this->builder = $builder; $this->query = $query; @@ -94,19 +89,14 @@ public function getQuery() return $this->query; } - /** - * @return Worker - */ - public function getWorker() + protected function buildRequest() { - if ($this->worker === null) { + if ($this->isBuilt === null) { if (!empty($this->query)) { $this->updateFromQuery(); } - $this->worker = $this->createWorker(); + $this->isBuilt = true; } - - return $this->worker; } protected function updateFromQuery() @@ -124,19 +114,9 @@ protected function updateFromQuery() $this->buildProtocolVersion(); } - abstract protected function createWorker(); - - public function send($options = []) - { - $handler = $this->builder->getHandler(); - $worker = $handler->send($this->getWorker(), $options); - - return new $this->responseClass($worker, $request); - } - protected function buildDbname() { - $this->dbname = $this->builder->db->name; + $this->dbname = $this->getDb()->name; } protected function buildAuth() @@ -209,6 +189,7 @@ public function getParts() { if (empty($this->parts)) { $this->getWorker(); + $this->buildRequest(); foreach (['dbname', 'method', 'uri', 'headers', 'body', 'version'] as $key) { $this->parts[$key] = $this->{$key}; } @@ -217,12 +198,33 @@ public function getParts() return $this->parts; } - public function send() + public function isRaw() + { + return !empty($this->query->options['raw']); + } + + protected function getHandler() { + $handler = $this->getDb()->getHandler(); + if ($handler === null) { + $handler = $this->createHandler(); + } + + return $handler; } - public function isRaw() + protected function createHandler() { - return !empty($this->query->options['raw']); + return new $this->handlerClass($this->getDb()->config); + } + + protected function getHandlerConfig() + { + return $this->getDb()->config; + } + + public function getDb() + { + return $this->builder->db; } } diff --git a/src/ProxyRequest.php b/src/ProxyRequest.php new file mode 100644 index 0000000..5e38880 --- /dev/null +++ b/src/ProxyRequest.php @@ -0,0 +1,47 @@ +builder->getHandler(); + $worker = $handler->send($this->getWorker(), $options); + + return new $this->responseClass($worker, $request); + } + + /** + * @return Worker + */ + public function getWorker() + { + if ($this->worker === null) { + $this->buildRequest(); + $this->worker = $this->createWorker(); + } + + return $this->worker; + } +} From e6916d2179813e0dc3f01a7688e0dc9cd52bfa68 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 08:13:16 +0200 Subject: [PATCH 28/53] csfixed --- .php_cs | 2 +- src/AbstractConnection.php | 9 ++++----- src/AbstractQueryBuilder.php | 2 +- src/AbstractRequest.php | 4 ++-- src/AbstractResponse.php | 2 +- src/ActiveDataProvider.php | 2 +- src/ActiveQuery.php | 6 ++---- src/ActiveRecord.php | 2 +- src/Collection.php | 4 ++-- src/Command.php | 2 +- src/ErrorResponseException.php | 2 +- src/Exception.php | 2 +- src/ProxyRequest.php | 4 ++-- src/Query.php | 9 +++------ src/QueryBuilderInterface.php | 8 ++------ src/config/hisite.php | 2 +- src/curl/Request.php | 8 ++++++++ src/curl/Response.php | 8 ++++++++ src/debug/DebugAction.php | 3 +-- src/debug/DebugPanel.php | 4 ++-- src/debug/Timing.php | 4 ++-- src/rest/Connection.php | 8 ++++++++ src/rest/QueryBuilder.php | 9 ++++++++- src/views/debug/detail.php | 2 +- tests/Mock.php | 2 +- tests/_bootstrap.php | 2 +- tests/unit/CommandTest.php | 2 +- tests/unit/ConnectionTest.php | 2 +- 28 files changed, 68 insertions(+), 48 deletions(-) diff --git a/.php_cs b/.php_cs index da57fc2..bc8b025 100644 --- a/.php_cs +++ b/.php_cs @@ -6,7 +6,7 @@ Tools to use API as ActiveRecord for Yii2 @link https://github.com/hiqdev/yii2-hiart @package yii2-hiart @license BSD-3-Clause -@copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) +@copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) EOF; return PhpCsFixer\Config::create() diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index 41c5a7d..655049a 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; @@ -19,14 +19,13 @@ /** * Abstract connection class. - * */ abstract class AbstractConnection extends Component { const EVENT_AFTER_OPEN = 'afterOpen'; /** - * @var string to be specified in concrete implementation. + * @var string to be specified in concrete implementation */ public $queryBuilderClass; @@ -46,7 +45,7 @@ abstract class AbstractConnection extends Component public $config = []; /** - * @var object request handler common for all requests of this connection. + * @var object request handler common for all requests of this connection */ protected $_handler; @@ -154,7 +153,7 @@ public function createCommand(array $config = []) } /** - * @return QueryBuilder the query builder for this connection. + * @return QueryBuilder the query builder for this connection */ public function getQueryBuilder() { diff --git a/src/AbstractQueryBuilder.php b/src/AbstractQueryBuilder.php index 7f2f795..b651f9f 100644 --- a/src/AbstractQueryBuilder.php +++ b/src/AbstractQueryBuilder.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; diff --git a/src/AbstractRequest.php b/src/AbstractRequest.php index 57cc4ae..949f658 100644 --- a/src/AbstractRequest.php +++ b/src/AbstractRequest.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; @@ -13,7 +13,7 @@ abstract class AbstractRequest implements \Serializable { /** - * @var string response implementation to be specified in concrete implementation. + * @var string response implementation to be specified in concrete implementation */ protected $responseClass; diff --git a/src/AbstractResponse.php b/src/AbstractResponse.php index 8b824f0..29645a2 100644 --- a/src/AbstractResponse.php +++ b/src/AbstractResponse.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; diff --git a/src/ActiveDataProvider.php b/src/ActiveDataProvider.php index 16af7ad..5b5e0af 100644 --- a/src/ActiveDataProvider.php +++ b/src/ActiveDataProvider.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; diff --git a/src/ActiveQuery.php b/src/ActiveQuery.php index a672a29..4c1000c 100644 --- a/src/ActiveQuery.php +++ b/src/ActiveQuery.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; @@ -13,7 +13,6 @@ use yii\db\ActiveQueryInterface; use yii\db\ActiveQueryTrait; use yii\db\ActiveRelationTrait; -use yii\helpers\ArrayHelper; class ActiveQuery extends Query implements ActiveQueryInterface { @@ -207,7 +206,7 @@ private function joinWithRelation($parent, $child) } } - public function select($columns, $option = NULL) + public function select($columns, $option = null) { $this->select = $columns; @@ -385,5 +384,4 @@ private function addInverseRelation($relatedModel) $inverseRelation = $relatedModel->getRelation($this->inverseOf); $relatedModel->populateRelation($this->inverseOf, $inverseRelation->multiple ? [$this->primaryModel] : $this->primaryModel); } - } diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index e660898..580f474 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; diff --git a/src/Collection.php b/src/Collection.php index c7b25d9..cde5f0a 100644 --- a/src/Collection.php +++ b/src/Collection.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; @@ -195,7 +195,7 @@ public function updateFormName() * } * 3) foreach ($selection as $id) { * $res[$id] = [reset($model->primaryKey()) => $id]; - * } + * }. * @param array|callable $data - the data to be proceeded. * If is callable - gets arguments: * - model diff --git a/src/Command.php b/src/Command.php index 51e7932..84d5394 100644 --- a/src/Command.php +++ b/src/Command.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; diff --git a/src/ErrorResponseException.php b/src/ErrorResponseException.php index c5bcb48..2c9be99 100644 --- a/src/ErrorResponseException.php +++ b/src/ErrorResponseException.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; diff --git a/src/Exception.php b/src/Exception.php index 47ed54c..a78f839 100644 --- a/src/Exception.php +++ b/src/Exception.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; diff --git a/src/ProxyRequest.php b/src/ProxyRequest.php index 5e38880..c577995 100644 --- a/src/ProxyRequest.php +++ b/src/ProxyRequest.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; @@ -18,7 +18,7 @@ abstract class ProxyRequest extends AbstractRequest protected $worker; /** - * @var string transport implementation to be specified in concrete implementation. + * @var string transport implementation to be specified in concrete implementation */ public $handlerClass; diff --git a/src/Query.php b/src/Query.php index 4a03f02..bb1b2ba 100644 --- a/src/Query.php +++ b/src/Query.php @@ -5,15 +5,12 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; -use Yii; -use yii\base\Component; use yii\db\QueryInterface; -use yii\helpers\ArrayHelper; /** * Query represents API query in a way that is independent from a concrete API. @@ -32,7 +29,7 @@ * - from: entity being queried, e.g. user * - join: data how to join with other entities * - other standard query options provided with QueryTrait: - * - where, limit, offset, orderBy, indexBy + * - where, limit, offset, orderBy, indexBy. */ class Query extends \yii\db\Query implements QueryInterface { @@ -52,7 +49,7 @@ class Query extends \yii\db\Query implements QueryInterface public static function instantiate($action, $from, array $options = []) { - $query = new static; + $query = new static(); return $query->action($action)->from($from)->options($options); } diff --git a/src/QueryBuilderInterface.php b/src/QueryBuilderInterface.php index f51fe10..47b5ba2 100644 --- a/src/QueryBuilderInterface.php +++ b/src/QueryBuilderInterface.php @@ -5,15 +5,11 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart; -use yii\base\InvalidParamException; -use yii\base\NotSupportedException; -use yii\helpers\ArrayHelper; - /** * QueryBuilder interface. * @@ -31,7 +27,7 @@ public function buildHeaders(Query $query); /** * Builds transport protocol version. - * @param Query $query + * @param Query $query * @return string */ public function buildProtocolVersion(Query $query); diff --git a/src/config/hisite.php b/src/config/hisite.php index d088ab9..8749be5 100644 --- a/src/config/hisite.php +++ b/src/config/hisite.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ return [ diff --git a/src/curl/Request.php b/src/curl/Request.php index ea58e20..404ea6c 100644 --- a/src/curl/Request.php +++ b/src/curl/Request.php @@ -1,4 +1,12 @@ getBaseUri() . '/'. $this->request->getUri(); + return $this->getBaseUri() . '/' . $this->request->getUri(); } public function getBaseUri() diff --git a/src/rest/Connection.php b/src/rest/Connection.php index 90f9816..fb86529 100644 --- a/src/rest/Connection.php +++ b/src/rest/Connection.php @@ -1,4 +1,12 @@ registerCss(<<registerCss(<<<'CSS' .string { color: green; } .number { color: darkorange; } .boolean { color: blue; } diff --git a/tests/Mock.php b/tests/Mock.php index 5b31c86..be03def 100644 --- a/tests/Mock.php +++ b/tests/Mock.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart\tests; diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php index 25e8a29..9ec37f2 100644 --- a/tests/_bootstrap.php +++ b/tests/_bootstrap.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ error_reporting(E_ALL & ~E_NOTICE); diff --git a/tests/unit/CommandTest.php b/tests/unit/CommandTest.php index c612716..c4807c3 100644 --- a/tests/unit/CommandTest.php +++ b/tests/unit/CommandTest.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart\tests\unit; diff --git a/tests/unit/ConnectionTest.php b/tests/unit/ConnectionTest.php index 1208546..36e23f9 100644 --- a/tests/unit/ConnectionTest.php +++ b/tests/unit/ConnectionTest.php @@ -5,7 +5,7 @@ * @link https://github.com/hiqdev/yii2-hiart * @package yii2-hiart * @license BSD-3-Clause - * @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) + * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ namespace hiqdev\hiart\tests\unit; From f0f2ee7d13b81009c6d8f244cc98bd96f93fb6a4 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 09:31:00 +0200 Subject: [PATCH 29/53] simplified `send` function in ProxyRequest --- src/ProxyRequest.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ProxyRequest.php b/src/ProxyRequest.php index c577995..3f90a4c 100644 --- a/src/ProxyRequest.php +++ b/src/ProxyRequest.php @@ -18,7 +18,7 @@ abstract class ProxyRequest extends AbstractRequest protected $worker; /** - * @var string transport implementation to be specified in concrete implementation + * @var string transport implementation to be specified in concrete implementation. */ public $handlerClass; @@ -26,8 +26,7 @@ abstract protected function createWorker(); public function send($options = []) { - $handler = $this->builder->getHandler(); - $worker = $handler->send($this->getWorker(), $options); + $worker = $this->getHandler()->send($this->getWorker(), $options); return new $this->responseClass($worker, $request); } From 4bd0fb171012f73e518bb2ca6ee7dbdfcbd8ad57 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 09:37:20 +0200 Subject: [PATCH 30/53] redone preparing config for db handler --- src/AbstractConnection.php | 16 ---------------- src/AbstractRequest.php | 8 +++++--- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index 655049a..47e1915 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -13,7 +13,6 @@ use Closure; use hiqdev\hiart\stream\Request; use yii\base\Component; -use yii\base\InvalidConfigException; use yii\base\InvalidParamException; use yii\helpers\Json; @@ -102,21 +101,6 @@ public function enableAuth() $this->_disabledAuth = false; } - /** - * {@inheritdoc} - * @throws InvalidConfigException - */ - public function init() - { - if (!$this->config['base_uri']) { - throw new InvalidConfigException('The `base_uri` config option must be set'); - } - - if (!isset($this->config['headers']['User-Agent'])) { - $this->config['headers']['User-Agent'] = 'HiArt/0.x'; - } - } - public function getBaseUri() { return $this->config['base_uri']; diff --git a/src/AbstractRequest.php b/src/AbstractRequest.php index 949f658..e6fa6fb 100644 --- a/src/AbstractRequest.php +++ b/src/AbstractRequest.php @@ -215,12 +215,14 @@ protected function getHandler() protected function createHandler() { - return new $this->handlerClass($this->getDb()->config); + $config = $this->prepareHandlerConfig($this->getDb()->config); + + return new $this->handlerClass($config); } - protected function getHandlerConfig() + protected function prepareHandlerConfig($config) { - return $this->getDb()->config; + return $config; } public function getDb() From f8fd2551f99d053151abf60f796c9724c253ac92 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 12:17:27 +0200 Subject: [PATCH 31/53] inited stream transport implementation --- src/stream/Request.php | 27 +++++++++++++++++++++++++++ src/stream/Response.php | 26 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/stream/Request.php create mode 100644 src/stream/Response.php diff --git a/src/stream/Request.php b/src/stream/Request.php new file mode 100644 index 0000000..507bb13 --- /dev/null +++ b/src/stream/Request.php @@ -0,0 +1,27 @@ + + */ +class Request extends AbstractRequest +{ + protected $workerClass = RequestWorker::class; + + protected function createWorker() + { + } +} diff --git a/src/stream/Response.php b/src/stream/Response.php new file mode 100644 index 0000000..9cb4b18 --- /dev/null +++ b/src/stream/Response.php @@ -0,0 +1,26 @@ + + */ +class Response extends AbstractResponse +{ + /** + * @var ResponseWorker + */ + protected $worker; +} From 7340b9c515fb483acaf2f96d1c87d9739336a8d5 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 12:17:50 +0200 Subject: [PATCH 32/53] fixed minor issues --- src/AbstractQueryBuilder.php | 2 +- src/curl/Response.php | 2 +- src/rest/QueryBuilder.php | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/AbstractQueryBuilder.php b/src/AbstractQueryBuilder.php index b651f9f..0b050c8 100644 --- a/src/AbstractQueryBuilder.php +++ b/src/AbstractQueryBuilder.php @@ -42,7 +42,7 @@ public function build(Query $query) public function createRequest($query) { - return new Request($this, $query); + return new $this->db->requestClass($this, $query); } /** diff --git a/src/curl/Response.php b/src/curl/Response.php index 9dbdf59..b4e6e14 100644 --- a/src/curl/Response.php +++ b/src/curl/Response.php @@ -13,7 +13,7 @@ use hiqdev\hiart\AbstractResponse; /** - * Guzzle response implementation. + * cURL response implementation. * * @author Andrii Vasyliev */ diff --git a/src/rest/QueryBuilder.php b/src/rest/QueryBuilder.php index c05a0d2..85a3bb0 100644 --- a/src/rest/QueryBuilder.php +++ b/src/rest/QueryBuilder.php @@ -10,7 +10,9 @@ namespace hiqdev\hiart\rest; -class QueryBuilder extends AbstractQueryBuilder +use hiqdev\hiart\Query; + +class QueryBuilder extends \hiqdev\hiart\AbstractQueryBuilder { /** * This function is for you to provide your authentication. From 8718fc3e60b442170cc634622062fb4d50f4ae83 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 12:25:51 +0200 Subject: [PATCH 33/53] renamed buildRequest -> build --- src/AbstractRequest.php | 4 ++-- src/ProxyRequest.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/AbstractRequest.php b/src/AbstractRequest.php index e6fa6fb..31e9f39 100644 --- a/src/AbstractRequest.php +++ b/src/AbstractRequest.php @@ -89,7 +89,7 @@ public function getQuery() return $this->query; } - protected function buildRequest() + protected function build() { if ($this->isBuilt === null) { if (!empty($this->query)) { @@ -189,7 +189,7 @@ public function getParts() { if (empty($this->parts)) { $this->getWorker(); - $this->buildRequest(); + $this->build(); foreach (['dbname', 'method', 'uri', 'headers', 'body', 'version'] as $key) { $this->parts[$key] = $this->{$key}; } diff --git a/src/ProxyRequest.php b/src/ProxyRequest.php index 3f90a4c..ee9c598 100644 --- a/src/ProxyRequest.php +++ b/src/ProxyRequest.php @@ -37,7 +37,7 @@ public function send($options = []) public function getWorker() { if ($this->worker === null) { - $this->buildRequest(); + $this->build(); $this->worker = $this->createWorker(); } From cd0f032f69998173ed3c404523d5365aba0b8d51 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 14:16:03 +0200 Subject: [PATCH 34/53] added stream transport implementation --- src/stream/Request.php | 66 ++++++++++++++++++++++++++++++++++++++++- src/stream/Response.php | 24 ++++++++++++--- 2 files changed, 85 insertions(+), 5 deletions(-) diff --git a/src/stream/Request.php b/src/stream/Request.php index 507bb13..e9bb8e1 100644 --- a/src/stream/Request.php +++ b/src/stream/Request.php @@ -11,6 +11,7 @@ namespace hiqdev\hiart\stream; use hiqdev\hiart\AbstractRequest; +use yii\helpers\Inflector; /** * PHP stream request implementation. @@ -21,7 +22,70 @@ class Request extends AbstractRequest { protected $workerClass = RequestWorker::class; - protected function createWorker() + public $defaultOptions = [ + 'http' => [ + 'ignore_errors' => true, + ], + 'ssl' => [ + 'verify_peer' => false, + ], + ]; + + public function send($options = []) { + $this->build(); + + try { + $context = stream_context_create($this->prepareContextOptions($options)); + $stream = fopen($this->getFullUri(), 'rb', false, $context); + $responseContent = stream_get_contents($stream); + // see http://php.net/manual/en/reserved.variables.httpresponseheader.php + $responseHeaders = $http_response_header; + fclose($stream); + } catch (\Exception $e) { + throw new Exception($e->getMessage(), $e->getCode(), $e); + } + + return new $this->responseClass($this, $responseContent, $responseHeaders); + } + + protected function prepareContextOptions($options) + { + $requestOptions = [ + 'http' => [ + 'method' => $this->method, + 'header' => $this->headers, + ], + ]; + + if (isset($this->body)) { + $requestOptions['http']['content'] = $this->body; + } + + $dbOptions = $this->convertContextOptions($this->getDb()->requestOptions); + $sendOptions = $this->convertContextOptions($options); + + return ArrayHelper::merge($this->defaultOptions, $dbOptions, $requestOptions, $sendOptions); + } + + /** + * Converts raw options to stream context options. + * @param array $options raw options. + * @return array stream context options. + */ + protected function convertContextOptions(array $options) + { + $contextOptions = []; + foreach ($options as $key => $value) { + $section = 'http'; + if (strpos($key, 'ssl') === 0) { + $section = 'ssl'; + $key = substr($key, 3); + } + $key = Inflector::underscore($key); + $contextOptions[$section][$key] = $value; + } + + return $contextOptions; } } diff --git a/src/stream/Response.php b/src/stream/Response.php index 9cb4b18..b46cb03 100644 --- a/src/stream/Response.php +++ b/src/stream/Response.php @@ -19,8 +19,24 @@ */ class Response extends AbstractResponse { - /** - * @var ResponseWorker - */ - protected $worker; + protected $rawData; + + protected $headers; + + public function __construct(Request $request, $rawData, array $headers) + { + $this->request = $request; + $this->rawData = $rawData; + $this->headers = $headers; + } + + public function getRawData() + { + return $this->rawData; + } + + public function getHeader($name) + { + return isset($this->headers[$name]) ? $this->headers[$name] : null; + } } From 5d536960238d4dd7228520032cc1960ab98408d5 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 14:16:37 +0200 Subject: [PATCH 35/53] refactored proxy transport --- src/AbstractConnection.php | 5 +++ src/AbstractRequest.php | 21 +++++++++++- src/AbstractResponse.php | 33 +++--------------- src/{ProxyRequest.php => proxy/Request.php} | 6 ++-- src/proxy/Response.php | 37 +++++++++++++++++++++ 5 files changed, 70 insertions(+), 32 deletions(-) rename src/{ProxyRequest.php => proxy/Request.php} (85%) create mode 100644 src/proxy/Response.php diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index 47e1915..51cd6a0 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -43,6 +43,11 @@ abstract class AbstractConnection extends Component */ public $config = []; + /** + * @var array request options will be use used in Request + */ + public $requestOptions = []; + /** * @var object request handler common for all requests of this connection */ diff --git a/src/AbstractRequest.php b/src/AbstractRequest.php index 31e9f39..48226ee 100644 --- a/src/AbstractRequest.php +++ b/src/AbstractRequest.php @@ -42,6 +42,7 @@ abstract class AbstractRequest implements \Serializable protected $version; protected $parts = []; + protected $fullUri; abstract public function send($options = []); @@ -66,6 +67,25 @@ public function getUri() return $this->uri; } + public function getFullUri() + { + if ($this->fullUri === null) { + $this->fullUri = $this->createFullUri(); + } + + return $this->fullUri; + } + + public function createFullUri() + { + return ($this->isFullUri($this->uri) ? '' : $this->db->baseUri) . $this->uri; + } + + public function isFullUri($uri) + { + return preg_match('/^https?:\\/\\//i', $uri); + } + public function getHeaders() { return $this->headers; @@ -188,7 +208,6 @@ public function unserialize($string) public function getParts() { if (empty($this->parts)) { - $this->getWorker(); $this->build(); foreach (['dbname', 'method', 'uri', 'headers', 'body', 'version'] as $key) { $this->parts[$key] = $this->{$key}; diff --git a/src/AbstractResponse.php b/src/AbstractResponse.php index 29645a2..7ef0f89 100644 --- a/src/AbstractResponse.php +++ b/src/AbstractResponse.php @@ -10,18 +10,12 @@ namespace hiqdev\hiart; -use Psr\Http\Message\ResponseInterface; use yii\helpers\Json; -class Response +abstract class AbstractResponse { /** - * @var ResponseInterface - */ - protected $worker; - - /** - * @var Request + * @var RequestInterface */ protected $request; @@ -32,17 +26,6 @@ class Response protected $isDecoded = false; - public function __construct(ResponseInterface $worker, Request $request) - { - $this->worker = $worker; - $this->request = $request; - } - - public function getWorker() - { - return $this->worker; - } - public function getRequest() { return $this->request; @@ -60,7 +43,7 @@ public function getData() public function decodeData() { - $data = $this->getBodyContents(); + $data = $this->getRawData(); if (!$this->isRaw() && $this->isJson()) { $data = Json::decode($data); } @@ -68,10 +51,7 @@ public function decodeData() return $data; } - public function getBodyContents() - { - return $this->worker->getBody()->getContents(); - } + abstract public function getRawData(); public function isRaw() { @@ -83,8 +63,5 @@ public function isJson() return preg_grep('|application/json|i', $this->getHeader('Content-Type')); } - public function getHeader($name) - { - return $this->worker->getHeader($name); - } + abstract public function getHeader($name); } diff --git a/src/ProxyRequest.php b/src/proxy/Request.php similarity index 85% rename from src/ProxyRequest.php rename to src/proxy/Request.php index ee9c598..7673666 100644 --- a/src/ProxyRequest.php +++ b/src/proxy/Request.php @@ -8,9 +8,9 @@ * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ -namespace hiqdev\hiart; +namespace hiqdev\hiart\proxy; -abstract class ProxyRequest extends AbstractRequest +abstract class Request extends \hiqdev\hiart\AbstractRequest { /** * @var object @@ -28,7 +28,7 @@ public function send($options = []) { $worker = $this->getHandler()->send($this->getWorker(), $options); - return new $this->responseClass($worker, $request); + return new $this->responseClass($this, $worker); } /** diff --git a/src/proxy/Response.php b/src/proxy/Response.php new file mode 100644 index 0000000..b895cd3 --- /dev/null +++ b/src/proxy/Response.php @@ -0,0 +1,37 @@ +request = $request; + $this->worker = $worker; + } + + public function getWorker() + { + return $this->worker; + } + + public function getHeader($name) + { + return $this->worker->getHeader($name); + } +} From 88b224eb069540598f53381da2fb731d91796574 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Sun, 22 Jan 2017 19:33:55 +0200 Subject: [PATCH 36/53] still refactoring --- composer.json | 3 +- src/AbstractConnection.php | 13 ------ src/AbstractRequest.php | 3 +- src/Command.php | 42 ++++++++++--------- ...xception.php => RequestErrorException.php} | 4 +- src/RequestInterface.php | 16 +++++++ src/ResponseErrorException.php | 28 +++++++++++++ src/ResponseInterface.php | 18 ++++++++ src/stream/Request.php | 4 +- 9 files changed, 92 insertions(+), 39 deletions(-) rename src/{ErrorResponseException.php => RequestErrorException.php} (87%) create mode 100644 src/RequestInterface.php create mode 100644 src/ResponseErrorException.php create mode 100644 src/ResponseInterface.php diff --git a/composer.json b/composer.json index 1bbb40f..3f467a1 100644 --- a/composer.json +++ b/composer.json @@ -44,8 +44,7 @@ } ], "require": { - "yiisoft/yii2": "~2.0", - "guzzlehttp/guzzle": "6.*" + "yiisoft/yii2": "~2.0" }, "require-dev": { "hiqdev/hidev-php": "<2.0", diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index 51cd6a0..422436d 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -172,19 +172,6 @@ public function setHandler($handler) $this->_handler = $handler; } - /** - * Sends given request. - * @param Request $request - * @param array $options - * @return Response - */ - public function send(Request $request, array $options = []) - { - $worker = $this->getHandler()->send($request->getWorker(), $options); - - return new Response($worker, $request); - } - /** * @return boolean */ diff --git a/src/AbstractRequest.php b/src/AbstractRequest.php index 48226ee..4675ec4 100644 --- a/src/AbstractRequest.php +++ b/src/AbstractRequest.php @@ -10,7 +10,7 @@ namespace hiqdev\hiart; -abstract class AbstractRequest implements \Serializable +abstract class AbstractRequest implements RequestInterface { /** * @var string response implementation to be specified in concrete implementation @@ -41,6 +41,7 @@ abstract class AbstractRequest implements \Serializable protected $body; protected $version; + protected $isBuilt = false; protected $parts = []; protected $fullUri; diff --git a/src/Command.php b/src/Command.php index 84d5394..5150c56 100644 --- a/src/Command.php +++ b/src/Command.php @@ -23,11 +23,11 @@ class Command extends \yii\base\Component public $db; /** - * @var Request request object + * @var RequestInterface request object */ protected $request; - public function setRequest(Request $request) + public function setRequest(RequestInterface $request) { $this->request = $request; @@ -44,38 +44,41 @@ public function search() { $this->request->getQuery()->addAction('search'); - return $this->execute(); + return $this->send(); } /** * Sends a request to create/insert data. * @param mixed $table entity to create * @param mixed $columns attributes of object to create + * @param array $params request parameters * @return $this */ - public function insert($table, $columns, array $options = []) + public function insert($table, $columns, array $params = []) { - $request = $this->db->getQueryBuilder()->insert($table, $columns, $options); + $request = $this->db->getQueryBuilder()->insert($table, $columns, $params); return $this->setRequest($request); } /** - * Sends a request to create/insert data. - * @param mixed $table entity to create - * @param mixed $columns attributes of object to create + * Sends a request to update data. + * @param mixed $table entity to update + * @param mixed $columns attributes of object to update + * @param array $condition + * @param array $params request parameters * @return $this */ - public function update($table, $columns, $condition = [], array $options = []) + public function update($table, $columns, $condition = [], array $params = []) { - $request = $this->db->getQueryBuilder()->update($table, $columns, $condition, $options); + $request = $this->db->getQueryBuilder()->update($table, $columns, $condition, $params); return $this->setRequest($request); } - public function delete($table, $condition, array $options = []) + public function delete($table, $condition, array $params = []) { - $request = $this->db->getQueryBuilder()->delete($table, $condition, $options); + $request = $this->db->getQueryBuilder()->delete($table, $condition, $params); return $this->setRequest($request); } @@ -85,29 +88,28 @@ public function delete($table, $condition, array $options = []) * @param string $action * @param string $table * @param mixed $body - * @param array $options + * @param array $params request parameters * @return mixed response data */ - public function perform($action, $table, $body = [], array $options = []) + public function perform($action, $table, $body = [], array $params = []) { - $request = $this->db->getQueryBuilder()->perform($action, $table, $body, $options); + $request = $this->db->getQueryBuilder()->perform($action, $table, $body, $params); $this->setRequest($request); - return $this->execute(); + return $this->send(); } /** * Executes the request. - * @param string $url URL - * @param mixed $body request parameters + * @param array $options send options * @return mixed response data */ - public function execute() + public function send($options = []) { $profile = serialize($this->request); $category = static::getProfileCategory(); Yii::beginProfile($profile, $category); - $response = $this->db->send($this->request); + $response = $this->request->send($options); Yii::endProfile($profile, $category); return $this->db->checkResponse($response); diff --git a/src/ErrorResponseException.php b/src/RequestErrorException.php similarity index 87% rename from src/ErrorResponseException.php rename to src/RequestErrorException.php index 2c9be99..47b9b67 100644 --- a/src/ErrorResponseException.php +++ b/src/RequestErrorException.php @@ -11,9 +11,9 @@ namespace hiqdev\hiart; /** - * Class ErrorResponseException. + * Request error exception. */ -class ErrorResponseException extends Exception +class RequestErrorException extends Exception { /** * @var array The API response diff --git a/src/RequestInterface.php b/src/RequestInterface.php new file mode 100644 index 0000000..1c7c1af --- /dev/null +++ b/src/RequestInterface.php @@ -0,0 +1,16 @@ +response = $this->errorInfo['response']; + } +} diff --git a/src/ResponseInterface.php b/src/ResponseInterface.php new file mode 100644 index 0000000..054894d --- /dev/null +++ b/src/ResponseInterface.php @@ -0,0 +1,18 @@ +getMessage(), $e->getCode(), $e); + throw new RequestErrorException($e->getMessage(), $e->getCode(), $e); } return new $this->responseClass($this, $responseContent, $responseHeaders); From 9ef2129bdc3e936da19f26c29c1ae6e2cfb5a713 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Mon, 23 Jan 2017 10:18:36 +0000 Subject: [PATCH 37/53] refactored exceptions --- src/RequestErrorException.php | 11 +++++------ src/ResponseErrorException.php | 11 +++++------ src/stream/Request.php | 3 ++- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/RequestErrorException.php b/src/RequestErrorException.php index 47b9b67..0880525 100644 --- a/src/RequestErrorException.php +++ b/src/RequestErrorException.php @@ -12,17 +12,16 @@ /** * Request error exception. + * + * For exceptions during request sending. */ class RequestErrorException extends Exception { /** - * @var array The API response + * @return RequestInterface */ - public $response; - - public function __construct($message, array $errorInfo = [], $code = 0, \Exception $previous = null) + public function getRequest() { - parent::__construct($message, $errorInfo, $code, $previous); - $this->response = $this->errorInfo['response']; + return $this->errorInfo['request']; } } diff --git a/src/ResponseErrorException.php b/src/ResponseErrorException.php index d041cfb..97ea331 100644 --- a/src/ResponseErrorException.php +++ b/src/ResponseErrorException.php @@ -12,17 +12,16 @@ /** * Response error exception. + * + * For exceptions during response processing. */ class ResponseErrorException extends Exception { /** - * @var array The API response + * @return ResponseInterface */ - public $response; - - public function __construct($message, array $errorInfo = [], $code = 0, \Exception $previous = null) + public function getResponse() { - parent::__construct($message, $errorInfo, $code, $previous); - $this->response = $this->errorInfo['response']; + return $this->errorInfo['response']; } } diff --git a/src/stream/Request.php b/src/stream/Request.php index f7ad2ab..aa8ba61 100644 --- a/src/stream/Request.php +++ b/src/stream/Request.php @@ -45,7 +45,8 @@ public function send($options = []) $responseHeaders = $http_response_header; fclose($stream); } catch (\Exception $e) { - throw new RequestErrorException($e->getMessage(), $e->getCode(), $e); + $errorInfo = ['request' => $this]; + throw new RequestErrorException($e->getMessage(), $errorInfo, $e->getCode(), $e); } return new $this->responseClass($this, $responseContent, $responseHeaders); From 80117689244bb0bbbbe4230ee8192c6fd05d3832 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Mon, 23 Jan 2017 11:28:25 +0000 Subject: [PATCH 38/53] add `Connection::userAgent` --- src/AbstractConnection.php | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index 422436d..f3575ea 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -38,15 +38,14 @@ abstract class AbstractConnection extends Component public $name = 'hiart'; - /** - * @var array connection config will be passed to handler - */ - public $config = []; + public $userAgent = 'HiArt/0.x'; + + public $baseUri; /** - * @var array request options will be use used in Request + * @var array transport config will be used in Request for handler or proxy request */ - public $requestOptions = []; + public $config = []; /** * @var object request handler common for all requests of this connection @@ -106,11 +105,6 @@ public function enableAuth() $this->_disabledAuth = false; } - public function getBaseUri() - { - return $this->config['base_uri']; - } - /** * Closes the connection when this component is being serialized. * @return array @@ -259,4 +253,13 @@ public function isError(Response $response) { return false; } + public function getBaseUri() + { + return $this->baseUri; + } + + public function getUserAgent() + { + return $this->userAgent; + } } From 2d4076c1b78ee5cd631f7cc58b5784e15113d2a6 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Mon, 23 Jan 2017 11:29:46 +0000 Subject: [PATCH 39/53] fixing stream transport --- src/AbstractRequest.php | 12 ++++++++++-- src/stream/Request.php | 27 ++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/src/AbstractRequest.php b/src/AbstractRequest.php index 4675ec4..a6443bb 100644 --- a/src/AbstractRequest.php +++ b/src/AbstractRequest.php @@ -41,7 +41,7 @@ abstract class AbstractRequest implements RequestInterface protected $body; protected $version; - protected $isBuilt = false; + protected $isBuilt; protected $parts = []; protected $fullUri; @@ -79,7 +79,7 @@ public function getFullUri() public function createFullUri() { - return ($this->isFullUri($this->uri) ? '' : $this->db->baseUri) . $this->uri; + return ($this->isFullUri($this->uri) ? '' : $this->getDb()->getBaseUri()) . $this->uri; } public function isFullUri($uri) @@ -169,6 +169,9 @@ protected function buildQueryParams() protected function buildHeaders() { $this->headers = $this->builder->buildHeaders($this->query); + if (empty($this->headers['User-Agent'])) { + $this->headers['User-Agent'] = $this->prepareUserAgent(); + } } protected function buildBody() @@ -245,6 +248,11 @@ protected function prepareHandlerConfig($config) return $config; } + protected function prepareUserAgent() + { + return $this->getDb()->getUserAgent(); + } + public function getDb() { return $this->builder->db; diff --git a/src/stream/Request.php b/src/stream/Request.php index aa8ba61..7967e59 100644 --- a/src/stream/Request.php +++ b/src/stream/Request.php @@ -22,7 +22,7 @@ */ class Request extends AbstractRequest { - protected $workerClass = RequestWorker::class; + protected $responseClass = Response::class; public $defaultOptions = [ 'http' => [ @@ -35,9 +35,9 @@ class Request extends AbstractRequest public function send($options = []) { - $this->build(); try { + $this->build(); $context = stream_context_create($this->prepareContextOptions($options)); $stream = fopen($this->getFullUri(), 'rb', false, $context); $responseContent = stream_get_contents($stream); @@ -56,8 +56,9 @@ protected function prepareContextOptions($options) { $requestOptions = [ 'http' => [ + 'protocol_version' => $this->version, 'method' => $this->method, - 'header' => $this->headers, + 'header' => static::composeHeaderLines($this->headers), ], ]; @@ -65,14 +66,14 @@ protected function prepareContextOptions($options) $requestOptions['http']['content'] = $this->body; } - $dbOptions = $this->convertContextOptions($this->getDb()->requestOptions); + $dbOptions = $this->convertContextOptions($this->getDb()->config); $sendOptions = $this->convertContextOptions($options); return ArrayHelper::merge($this->defaultOptions, $dbOptions, $requestOptions, $sendOptions); } /** - * Converts raw options to stream context options. + * Composes stream context options from raw options. * @param array $options raw options. * @return array stream context options. */ @@ -91,4 +92,20 @@ protected function convertContextOptions(array $options) return $contextOptions; } + + public static function composeHeaderLines($headers) + { + $result = []; + foreach ($headers as $name => $values) { + $name = str_replace(' ', '-', ucwords(str_replace('-', ' ', $name))); + if (is_string($values)) { + $values = [$values]; + } + foreach ($values as $value) { + $result[] = "$name: $value"; + } + } + + return $result; + } } From d632d37bb9d077afec224ce98ee7d4fc5691ef2a Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Mon, 23 Jan 2017 14:18:50 +0000 Subject: [PATCH 40/53] stream transport looks working --- src/AbstractConnection.php | 15 +++++++------- src/AbstractResponse.php | 4 ++-- src/ActiveRecord.php | 29 ++++++++++++++++----------- src/Command.php | 3 ++- src/ResponseInterface.php | 4 ++++ src/rest/Connection.php | 18 ++++++++++++++++- src/stream/Response.php | 40 ++++++++++++++++++++++++++++++++++++-- 7 files changed, 88 insertions(+), 25 deletions(-) diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index f3575ea..8012ab8 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -213,11 +213,11 @@ public function setErrorChecker($checker) /** * Checks response with checkError method and raises exception if error. - * @param Response $response response data from API + * @param ResponseInterface $response response data from API * @throws ErrorResponseException * @return mixed response data */ - public function checkResponse(Response $response) + public function checkResponse(ResponseInterface $response) { $error = $this->checkError($response); if ($error) { @@ -226,16 +226,14 @@ public function checkResponse(Response $response) 'response' => $response->getData(), ]); } - - return $response->getData(); } /** * Checks response with errorChecker callback and returns error text if error. - * @param Response $response + * @param ResponseInterface $response * @return string|false error text or false */ - public function checkError(Response $response) + public function checkError(ResponseInterface $response) { if (isset($this->_errorChecker)) { return call_user_func($this->_errorChecker, $response); @@ -246,13 +244,14 @@ public function checkError(Response $response) /** * Default error checker. TODO check something in response? - * @param Response $response + * @param ResponseInterface $response * @return bool */ - public function isError(Response $response) + public function isError(ResponseInterface $response) { return false; } + public function getBaseUri() { return $this->baseUri; diff --git a/src/AbstractResponse.php b/src/AbstractResponse.php index 7ef0f89..cdcacce 100644 --- a/src/AbstractResponse.php +++ b/src/AbstractResponse.php @@ -12,7 +12,7 @@ use yii\helpers\Json; -abstract class AbstractResponse +abstract class AbstractResponse implements ResponseInterface { /** * @var RequestInterface @@ -60,7 +60,7 @@ public function isRaw() public function isJson() { - return preg_grep('|application/json|i', $this->getHeader('Content-Type')); + return !empty(preg_grep('|application/json|i', $this->getHeader('Content-Type'))); } abstract public function getHeader($name); diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index 580f474..1c04e15 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -154,20 +154,27 @@ public function getPrimaryValue() } /** - * Returns the list of all attribute names of the model. - * - * This method must be overridden by child classes to define available attributes. - * - * Attributes are names of fields of the corresponding API object. - * The primaryKey for HiArt documents is the `id` field by default which is not part of the attributes. - * - * @throws \yii\base\InvalidConfigException if not overridden in a child class - * - * @return string[] list of attribute names + * Returns the list of attribute names. + * By default, this method returns all attributes mentioned in rules. + * You may override this method to change the default behavior. + * @return string[] list of attribute names. */ public function attributes() { - throw new InvalidConfigException('The attributes() method of HiArt ActiveRecord has to be implemented by child classes.'); + $attributes = []; + foreach ($this->rules() as $rule) { + if (is_string(reset($rule))) { + continue; + } + foreach (reset($rule) as $attribute) { + if (substr_compare($attribute, '!', 0, 1) === 0) { + $attribute = mb_substr($attribute, 1); + } + $attributes[$attribute] = $attribute; + } + } + + return array_values($attributes); } /** diff --git a/src/Command.php b/src/Command.php index 5150c56..4303da1 100644 --- a/src/Command.php +++ b/src/Command.php @@ -111,8 +111,9 @@ public function send($options = []) Yii::beginProfile($profile, $category); $response = $this->request->send($options); Yii::endProfile($profile, $category); + $this->db->checkResponse($response); - return $this->db->checkResponse($response); + return $response->getData(); } public static function getProfileCategory() diff --git a/src/ResponseInterface.php b/src/ResponseInterface.php index 054894d..72cd5a4 100644 --- a/src/ResponseInterface.php +++ b/src/ResponseInterface.php @@ -12,6 +12,10 @@ interface ResponseInterface { + public function getRequest(); + + public function getData(); + public function getRawData(); public function getHeader($name); diff --git a/src/rest/Connection.php b/src/rest/Connection.php index fb86529..0e53fd5 100644 --- a/src/rest/Connection.php +++ b/src/rest/Connection.php @@ -10,7 +10,23 @@ namespace hiqdev\hiart\rest; -class Connection extends AbstractConnection +use hiqdev\hiart\ResponseInterface; +use hiqdev\hiart\ResponseErrorException; + +class Connection extends \hiqdev\hiart\AbstractConnection { public $queryBuilderClass = QueryBuilder::class; + + public function checkResponse(ResponseInterface $response) + { + $code = $response->getStatusCode(); + if ($code>=200 && $code<300) { + return; + } + + throw new ResponseErrorException($response->getReasonPhrase(), [ + 'request' => $response->getRequest()->getParts(), + 'response' => $response->getData(), + ], $code); + } } diff --git a/src/stream/Response.php b/src/stream/Response.php index b46cb03..373eb46 100644 --- a/src/stream/Response.php +++ b/src/stream/Response.php @@ -23,11 +23,15 @@ class Response extends AbstractResponse protected $headers; - public function __construct(Request $request, $rawData, array $headers) + protected $statusCode; + + protected $reasonPhrase; + + public function __construct(Request $request, $rawData, array $rawHeaders) { $this->request = $request; $this->rawData = $rawData; - $this->headers = $headers; + $this->headers = $this->parseHeaders($rawHeaders); } public function getRawData() @@ -37,6 +41,38 @@ public function getRawData() public function getHeader($name) { + $name = strtolower($name); + return isset($this->headers[$name]) ? $this->headers[$name] : null; } + + public function parseHeaders($headers) + { + foreach ($headers as $header) { + if (strncmp($header, 'HTTP/', 5) === 0) { + $parts = explode(' ', $header, 3); + $this->version = substr($parts[0], 6); + $this->statusCode = $parts[1]; + $this->reasonPhrase = $parts[2]; + } elseif (($pos = strpos($header, ':')) !== false) { + $name = strtolower(trim(substr($header, 0, $pos))); + $value = trim(substr($header, $pos + 1)); + $result[$name][] = $value; + } else { + $result['raw'][] = $header; + } + } + + return $result; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getReasonPhrase() + { + return $this->reasonPhrase; + } } From d367d14a17d0a9d03fd652565fd1ed6aeea3a30b Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Mon, 23 Jan 2017 15:45:23 +0000 Subject: [PATCH 41/53] fixed getting protocol version in stream Response --- src/stream/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/Response.php b/src/stream/Response.php index 373eb46..4685cbb 100644 --- a/src/stream/Response.php +++ b/src/stream/Response.php @@ -51,7 +51,7 @@ public function parseHeaders($headers) foreach ($headers as $header) { if (strncmp($header, 'HTTP/', 5) === 0) { $parts = explode(' ', $header, 3); - $this->version = substr($parts[0], 6); + $this->version = substr($parts[0], 5); $this->statusCode = $parts[1]; $this->reasonPhrase = $parts[2]; } elseif (($pos = strpos($header, ':')) !== false) { From a766a4478d6a4082346c2565a5e62f4b79d20944 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Mon, 23 Jan 2017 15:46:12 +0000 Subject: [PATCH 42/53] added `prepare()` in ActiveRecord, fixed `attributes()` --- src/ActiveQuery.php | 4 ++++ src/ActiveRecord.php | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/ActiveQuery.php b/src/ActiveQuery.php index 4c1000c..fbc5577 100644 --- a/src/ActiveQuery.php +++ b/src/ActiveQuery.php @@ -98,10 +98,14 @@ public function createCommand($db = null) /** * Prepares query for use. See NOTE. + * @param QueryBuilder $builder * @return static */ public function prepare($builder = null) { + $class = $this->modelClass; + $class::prepare($this, $builder); + // NOTE: because the same ActiveQuery may be used to build different SQL statements // (e.g. by ActiveDataProvider, one for count query, the other for row data query, // it is important to make sure the same ActiveQuery can be used to build SQL statements diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index 1c04e15..4128bbd 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -44,6 +44,14 @@ public static function find() return new $class(get_called_class()); } + /** + * This function is called from `Query::prepare`. + * You can redefine it to get desired behavior. + */ + public static function prepare(Query $query, QueryBuilderInterface $builder) + { + } + public function isScenarioDefault() { return $this->scenario === static::SCENARIO_DEFAULT; @@ -163,14 +171,15 @@ public function attributes() { $attributes = []; foreach ($this->rules() as $rule) { - if (is_string(reset($rule))) { - continue; + $names = reset($rule); + if (is_string($names)) { + $names = [$names]; } - foreach (reset($rule) as $attribute) { - if (substr_compare($attribute, '!', 0, 1) === 0) { - $attribute = mb_substr($attribute, 1); + foreach ($names as $name) { + if (substr_compare($name, '!', 0, 1) === 0) { + $name = mb_substr($name, 1); } - $attributes[$attribute] = $attribute; + $attributes[$name] = $name; } } From 1cd3d7b107ce5bf275bdb4ef14f99cf513a05992 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Mon, 23 Jan 2017 17:34:50 +0000 Subject: [PATCH 43/53] changed `getDb()` --- src/AbstractConnection.php | 8 ++++++++ src/ActiveRecord.php | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index 8012ab8..16d0f7e 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -11,6 +11,7 @@ namespace hiqdev\hiart; use Closure; +use Yii; use hiqdev\hiart\stream\Request; use yii\base\Component; use yii\base\InvalidParamException; @@ -36,6 +37,8 @@ abstract class AbstractConnection extends Component public $activeQueryClass = ActiveQuery::class; + public static $dbname = 'hiart'; + public $name = 'hiart'; public $userAgent = 'HiArt/0.x'; @@ -74,6 +77,11 @@ abstract class AbstractConnection extends Component */ protected $_errorChecker; + public static function getDb($dbname = null) + { + return Yii::$app->get($dbname ?: static::$dbname); + } + public function setAuth($auth) { $this->_auth = $auth; diff --git a/src/ActiveRecord.php b/src/ActiveRecord.php index 4128bbd..c4b096a 100644 --- a/src/ActiveRecord.php +++ b/src/ActiveRecord.php @@ -10,7 +10,7 @@ namespace hiqdev\hiart; -use Yii; +use hiqdev\hiart\AbstractConnection; use yii\base\InvalidConfigException; use yii\base\NotSupportedException; use yii\db\ActiveQueryInterface; @@ -30,7 +30,7 @@ class ActiveRecord extends BaseActiveRecord */ public static function getDb() { - return Yii::$app->get('hiart'); + return AbstractConnection::getDb(); } /** From ecc46d203905fc8ca92d35d67a5f4f34bb0152c0 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 09:52:42 +0000 Subject: [PATCH 44/53] added ConnectionInterface and DI configuration --- src/AbstractConnection.php | 4 ++-- src/ConnectionInterface.php | 31 +++++++++++++++++++++++++++++++ src/config/hisite.php | 16 +++++++++++++++- src/config/params.php | 7 +++++++ 4 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 src/ConnectionInterface.php create mode 100644 src/config/params.php diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index 16d0f7e..75e8bfe 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -20,7 +20,7 @@ /** * Abstract connection class. */ -abstract class AbstractConnection extends Component +abstract class AbstractConnection extends Component implements ConnectionInterface { const EVENT_AFTER_OPEN = 'afterOpen'; @@ -79,7 +79,7 @@ abstract class AbstractConnection extends Component public static function getDb($dbname = null) { - return Yii::$app->get($dbname ?: static::$dbname); + return $dbname ? Yii::$app->get($dbname) : Yii::$container->get(ConnectionInterface::class); } public function setAuth($auth) diff --git a/src/ConnectionInterface.php b/src/ConnectionInterface.php new file mode 100644 index 0000000..2c66373 --- /dev/null +++ b/src/ConnectionInterface.php @@ -0,0 +1,31 @@ + array_filter([ 'debug' => empty($params['debug.enabled']) ? null : [ 'panels' => [ @@ -18,4 +18,18 @@ ], ], ]), + 'components' => array_filter([ + $params['hiart.dbname'] => array_filter([ + 'class' => \hiqdev\hiart\hiart\Connection::class, + 'name' => $params['hiart.dbname'], + 'requestClass' => $params['hiart.requestClass'], + ]), + ]), + 'container' => [ + 'singletons' => [ + \hiqdev\hiart\ConnectionInterface::class => function () { + return Yii::$app->get($params['hiart.dbname']); + }, + ], + ], ]; diff --git a/src/config/params.php b/src/config/params.php new file mode 100644 index 0000000..aee7f5c --- /dev/null +++ b/src/config/params.php @@ -0,0 +1,7 @@ + true, + 'hiart.dbname' => 'hiart', + 'hiart.requestClass' => \hiqdev\hiart\stream\Request::class, +]; From e1563ba5f3fc54c0c2212f72e069565fa480111b Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 10:00:22 +0000 Subject: [PATCH 45/53] fixed configs --- composer.json | 5 ++++- src/config/{hisite.php => common.php} | 0 2 files changed, 4 insertions(+), 1 deletion(-) rename src/config/{hisite.php => common.php} (100%) diff --git a/composer.json b/composer.json index 3f467a1..53d351f 100644 --- a/composer.json +++ b/composer.json @@ -62,7 +62,10 @@ }, "extra": { "config-plugin": { - "hisite": "src/config/hisite.php" + "params": "src/config/params.php", + "common": "src/config/common.php", + "hisite": "src/config/hisite.php", + "hidev": "src/config/hidev.php" } }, "repositories": [ diff --git a/src/config/hisite.php b/src/config/common.php similarity index 100% rename from src/config/hisite.php rename to src/config/common.php From e72ea42c90744dca334b5516be1e6b35b4cef232 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 10:05:09 +0000 Subject: [PATCH 46/53] fixed configs again --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 53d351f..af3edec 100644 --- a/composer.json +++ b/composer.json @@ -64,8 +64,8 @@ "config-plugin": { "params": "src/config/params.php", "common": "src/config/common.php", - "hisite": "src/config/hisite.php", - "hidev": "src/config/hidev.php" + "hisite": "src/config/common.php", + "hidev": "src/config/common.php" } }, "repositories": [ From e1aab41511290928c2cb00cafc4a3b176a56ef22 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 10:07:15 +0000 Subject: [PATCH 47/53] fixed configs again --- composer.json | 4 ++-- src/config/hidev.php | 11 +++++++++++ src/config/hisite.php | 11 +++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 src/config/hidev.php create mode 100644 src/config/hisite.php diff --git a/composer.json b/composer.json index af3edec..53d351f 100644 --- a/composer.json +++ b/composer.json @@ -64,8 +64,8 @@ "config-plugin": { "params": "src/config/params.php", "common": "src/config/common.php", - "hisite": "src/config/common.php", - "hidev": "src/config/common.php" + "hisite": "src/config/hisite.php", + "hidev": "src/config/hidev.php" } }, "repositories": [ diff --git a/src/config/hidev.php b/src/config/hidev.php new file mode 100644 index 0000000..b01b459 --- /dev/null +++ b/src/config/hidev.php @@ -0,0 +1,11 @@ + Date: Tue, 24 Jan 2017 10:12:05 +0000 Subject: [PATCH 48/53] and fixed config once more again --- src/config/hidev.php | 2 +- src/config/hisite.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/hidev.php b/src/config/hidev.php index b01b459..3df0d54 100644 --- a/src/config/hidev.php +++ b/src/config/hidev.php @@ -8,4 +8,4 @@ * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ -return require './common.php'; +return require __DIR__ . '/common.php'; diff --git a/src/config/hisite.php b/src/config/hisite.php index b01b459..3df0d54 100644 --- a/src/config/hisite.php +++ b/src/config/hisite.php @@ -8,4 +8,4 @@ * @copyright Copyright (c) 2015-2017, HiQDev (http://hiqdev.com/) */ -return require './common.php'; +return require __DIR__ . '/common.php'; From d128a273919a5a6fcc83422de7c00b6bbdc7046c Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 15:54:15 +0000 Subject: [PATCH 49/53] improved Connection::getBaseUri to add trailing slash if uri is domain only --- src/AbstractConnection.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/AbstractConnection.php b/src/AbstractConnection.php index 75e8bfe..3817eb6 100644 --- a/src/AbstractConnection.php +++ b/src/AbstractConnection.php @@ -260,8 +260,21 @@ public function isError(ResponseInterface $response) return false; } + /** + * Return API base uri. + * Adds trailing slash if uri is domain only. + * @return string + */ public function getBaseUri() { + static $checked; + if (empty($checked)) { + if (count(explode('/', $this->baseUri, 4)) === 3) { + $this->baseUri .= '/'; + } + $checked = true; + } + return $this->baseUri; } From 4fb00e7d2ec1daae11d20b6756bf683a903c4de6 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 15:57:23 +0000 Subject: [PATCH 50/53] improved config and params --- src/config/common.php | 8 +++++--- src/config/params.php | 9 ++++++--- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/config/common.php b/src/config/common.php index 1eae03a..0a96031 100644 --- a/src/config/common.php +++ b/src/config/common.php @@ -20,15 +20,17 @@ ]), 'components' => array_filter([ $params['hiart.dbname'] => array_filter([ - 'class' => \hiqdev\hiart\hiart\Connection::class, - 'name' => $params['hiart.dbname'], + 'class' => $params['hiart.class'], 'requestClass' => $params['hiart.requestClass'], + 'name' => $params['hiart.dbname'], + 'config' => $params['hiart.config'], + 'baseUri' => $params['hiart.baseUri'], ]), ]), 'container' => [ 'singletons' => [ \hiqdev\hiart\ConnectionInterface::class => function () { - return Yii::$app->get($params['hiart.dbname']); + return Yii::$app->get(Yii::$app->params['hiart.dbname']); }, ], ], diff --git a/src/config/params.php b/src/config/params.php index aee7f5c..6a31893 100644 --- a/src/config/params.php +++ b/src/config/params.php @@ -1,7 +1,10 @@ true, - 'hiart.dbname' => 'hiart', - 'hiart.requestClass' => \hiqdev\hiart\stream\Request::class, + 'hiart.enabled' => true, + 'hiart.class' => \hiqdev\hiart\rest\Connection::class, + 'hiart.requestClass' => \hiqdev\hiart\stream\Request::class, + 'hiart.dbname' => 'hiart', + 'hiart.config' => [], + 'hiart.baseUri' => null, ]; From eaf137c8a54e0a3a6340f70ac84b349d2e307368 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 16:25:42 +0000 Subject: [PATCH 51/53] improved `AbstractRequest::getDb` --- src/AbstractRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AbstractRequest.php b/src/AbstractRequest.php index a6443bb..c1c3c7e 100644 --- a/src/AbstractRequest.php +++ b/src/AbstractRequest.php @@ -255,6 +255,6 @@ protected function prepareUserAgent() public function getDb() { - return $this->builder->db; + return isset($this->builder) ? $this->builder->db : AbstractConnection::getDb($this->dbname); } } From 67297d4553508bea316af42e9f16557b6bd155cd Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 16:26:01 +0000 Subject: [PATCH 52/53] removed getBaseUri from debug, used getFullUri from request --- src/debug/DebugPanel.php | 9 --------- src/debug/Timing.php | 14 ++------------ 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/src/debug/DebugPanel.php b/src/debug/DebugPanel.php index 2796b3b..4ef5755 100644 --- a/src/debug/DebugPanel.php +++ b/src/debug/DebugPanel.php @@ -65,15 +65,6 @@ public function getDetail() ]); } - public function getBaseUri($dbname) - { - try { - return Yii::$app->get($dbname)->getBaseUri(); - } catch (InvalidConfigException $e) { - return null; - } - } - private $_timings; public function getTimings() diff --git a/src/debug/Timing.php b/src/debug/Timing.php index b71360b..58e6ba7 100644 --- a/src/debug/Timing.php +++ b/src/debug/Timing.php @@ -70,7 +70,7 @@ public function getMethod() public function getUrlEncoded() { - return Html::encode($this->getFullUri()); + return Html::encode($this->request->getFullUri()); } public function getBodyEncoded() @@ -110,20 +110,10 @@ public function getRunLink() public function getNewTabLink() { - $uri = $this->getFullUri(); + $uri = $this->request->getFullUri(); $sign = strpos($uri, '?') === false ? '?' : ''; $newTabUrl = rtrim($uri, '&') . $sign . $this->request->getBody(); return Html::a('to new tab', $newTabUrl, ['target' => '_blank']); } - - public function getFullUri() - { - return $this->getBaseUri() . '/' . $this->request->getUri(); - } - - public function getBaseUri() - { - return $this->panel->getBaseUri($this->request->getDbname()); - } } From ebab31a56b3ddf65480a9d63e3215f1365361775 Mon Sep 17 00:00:00 2001 From: Andrii Vasyliev Date: Tue, 24 Jan 2017 17:00:29 +0000 Subject: [PATCH 53/53] adding yii2-hiart-github for tests --- composer.json | 1 + phpunit.xml.dist | 3 +++ 2 files changed, 4 insertions(+) diff --git a/composer.json b/composer.json index 53d351f..7ecb91f 100644 --- a/composer.json +++ b/composer.json @@ -47,6 +47,7 @@ "yiisoft/yii2": "~2.0" }, "require-dev": { + "hiqdev/yii2-hiart-github": "dev-master", "hiqdev/hidev-php": "<2.0", "hiqdev/hidev-hiqdev": "<2.0" }, diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 60413df..6613922 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -4,6 +4,9 @@ ./tests/unit/ + + ./vendor/hiqdev/hidev-hiart/tests/functional/ +
    TimeUrl / QueryRun Query on nodeTimeUrl / QueryRun Query

    getDuration() ?> + getMethod() ?> getUrlEncoded() ?>
    +

    getBodyEncoded() ?>

    + getTrace() ?> +
    + getRunLink() ?>
    + getNewTabLink() ?> +