From 7f7932398fa40e201e3c4b4ee273dc780bc8eed4 Mon Sep 17 00:00:00 2001 From: Clyde Cox Date: Tue, 26 Jun 2018 18:00:28 -0400 Subject: [PATCH 1/6] Use laravel Cache driver in place of file and redis code. --- src/Config/config.php | 1 - src/Vtiger.php | 114 ++++++++++++------------------------------ 2 files changed, 33 insertions(+), 82 deletions(-) diff --git a/src/Config/config.php b/src/Config/config.php index 53ded3f..89da684 100644 --- a/src/Config/config.php +++ b/src/Config/config.php @@ -5,7 +5,6 @@ 'url' => 'path/to/vtiger/webservice.php', 'username' => '', 'accesskey' => '', - 'sessiondriver' => 'file', //reddis or file 'persistconnection' => true, 'max_retries' => 10 ]; diff --git a/src/Vtiger.php b/src/Vtiger.php index 4aaf78a..636f4c0 100644 --- a/src/Vtiger.php +++ b/src/Vtiger.php @@ -7,10 +7,8 @@ use InvalidArgumentException; use Mockery\CountValidator\Exception; use Psr\Http\Message\ResponseInterface; -use \Illuminate\Contracts\Filesystem\FileNotFoundException; -use Storage; use Config; -use Redis; +use Cache; /** * Laravel wrapper for the VTgier API @@ -33,9 +31,6 @@ class Vtiger /** @var string */ protected $accessKey; - /** @var string */ - protected $sessionDriver; - /** @var string */ protected $persistConnection; @@ -56,7 +51,6 @@ public function __construct() $this->url = Config::get('vtiger.url'); $this->username = Config::get('vtiger.username'); $this->accessKey = Config::get('vtiger.accesskey'); - $this->sessionDriver = Config::get('vtiger.sessiondriver'); $this->persistConnection = Config::get('vtiger.persistconnection'); $this->maxRetries = Config::get('vtiger.max_retries'); @@ -65,14 +59,13 @@ public function __construct() 1 => new VtigerErrorElement('API request did not complete correctly - Response code: ', 1), 2 => new VtigerErrorElement('Success property not set on VTiger response', 2), 3 => new VtigerErrorElement('Error property not set on VTiger response when success is false', 3), - 4 => new VtigerErrorElement('Session driver type of ' . $this->sessionDriver . ' is not supported', 4), 5 => new VtigerErrorElement('Could not complete login request within ' . $this->maxRetries . ' tries', 5), 6 => new VtigerErrorElement( 'Could not complete get token request within ' . $this->maxRetries . ' tries', 6 ), 7 => new VtigerErrorElement('Guzzle ran into problems - ', 7), - 8 => new VtigerErrorElement('Redis problem - ', 8), + 8 => new VtigerErrorElement('Laravel Cache problem', 8), ]; try { @@ -80,7 +73,6 @@ public function __construct() } catch (InvalidArgumentException $e) { throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); } - } /** @@ -109,26 +101,11 @@ public function connection($url, $username, $accessKey) */ protected function sessionId() { - // Check the session file exists - switch ($this->sessionDriver) { - case "file": - if (Storage::disk('local')->exists('session.json')) { - try { - $sessionData = json_decode(Storage::disk('local')->get('session.json')); - } catch (FileNotFoundException $e) { - throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); - } - } - break; - case "redis": - $sessionData = json_decode(Redis::get('clystnet_vtiger')); + // Get the sessionData from the cache + $sessionData = json_decode(Cache::get('clystnet_vtiger')); - if (!$sessionData) { - throw VtigerError::init($this->vTigerErrors, 8, 'Could not get the session data from index clystnet_vtiger'); - } - break; - default: - throw new VtigerError("Session driver type of " . $this->sessionDriver . " is not supported", 4); + if (!$sessionData) { + throw VtigerError::init($this->vTigerErrors, 8, 'Could not get the session data from index clystnet_vtiger'); } if (isset($sessionData)) { @@ -202,18 +179,10 @@ protected function login($sessionData) if ($response->getStatusCode() !== 200 || !$loginResult->success) { if (!$loginResult->success) { if ($loginResult->error->code == "INVALID_USER_CREDENTIALS" || $loginResult->error->code == "INVALID_SESSIONID") { - if ($this->sessionDriver == 'file') { - if (Storage::disk('local')->exists('session.json')) { - try { - Storage::disk('local')->delete('session.json'); - } catch (FileNotFoundException $e) { - throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); - } - } - } elseif ($this->sessionDriver == 'redis') { - if (Redis::del('clystnet_vtiger') < 1) { - throw VtigerError::init($this->vTigerErrors, 8, 'Nothing to delete for index clystnet_vtiger'); - } + if (!Cache::has('clystnet_vtiger')) { + throw VtigerError::init($this->vTigerErrors, 8, 'Nothing to delete for index clystnet_vtiger'); + } else { + Cache::forget('clystnet_vtiger'); } } else { $this->_processResult($response); @@ -225,26 +194,13 @@ protected function login($sessionData) // login ok so get sessionid and update our session $sessionId = $loginResult->result->sessionName; - switch ($this->sessionDriver) { - case "file": - if (Storage::disk('local')->exists('session.json')) { - try { - $json = json_decode(Storage::disk('local')->get('session.json')); - $json->sessionid = $sessionId; - Storage::disk('local')->put('session.json', json_encode($json)); - } catch (FileNotFoundException $e) { - throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); - } - } - break; - case "redis": - Redis::incr('loggedin'); - $json = json_decode(Redis::get('clystnet_vtiger')); - $json->sessionid = $sessionId; - Redis::set('clystnet_vtiger', json_encode($json)); - break; - default: - throw new VtigerError("Session driver type of " . $this->sessionDriver . " is not supported", 4); + + if (Cache::has('clystnet_vtiger')) { + $json = json_decode(Cache::pull('clystnet_vtiger')); + $json->sessionid = $sessionId; + Cache::forever('clystnetVtiger', json_encode($json)); + } else { + throw VtigerError::init($this->vTigerErrors, 8, 'There is no key for index clystnet_vtiger.'); } } @@ -262,18 +218,9 @@ protected function storeSession() $updated = $this->getToken(); $output = (object)$updated; - if ($this->sessionDriver == 'file') { - try { - Storage::disk('local')->put('session.json', json_encode($output)); - } catch (FileNotFoundException $e) { - throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); - } - } elseif ($this->sessionDriver == 'redis') { - $setResult = Redis::set('clystnet_vtiger', json_encode($output)); - - if (!$setResult) { - throw VtigerError::init($this->vTigerErrors, 8, 'Could not set the session data in index clystnet_vtiger'); - } + $cacheResult = Cache::forever('clystnet_vtiger', json_encode($output)); + if (!$cacheResult) { + throw VtigerError::init($this->vTigerErrors, 8, 'Could not set the session data in index clystnet_vtiger'); } return $output; @@ -337,12 +284,15 @@ protected function close($sessionId) try { // send a request to close current connection $response = $this->guzzleClient->request( - 'POST', $this->url, [ + 'POST', + $this->url, + [ 'query' => [ 'operation' => 'logout', 'sessionName' => $sessionId ] - ]); + ] + ); } catch (GuzzleException $e) { throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); } @@ -402,7 +352,7 @@ public function retrieve($id) 'id' => $id ] ]); - } catch (GuzzleException $e) { + } catch (GuzzleException $e) { throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); } @@ -444,7 +394,7 @@ public function create($elem, $data) 'elementType' => $elem ] ]); - } catch (GuzzleException $e) { + } catch (GuzzleException $e) { throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); } @@ -476,7 +426,7 @@ public function update($object) 'element' => json_encode($object), ] ]); - } catch (GuzzleException $e) { + } catch (GuzzleException $e) { throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); } @@ -531,13 +481,16 @@ public function describe($elementType) try { // send a request to describe a module (which returns a list of available fields) for a Vtiger module $response = $this->guzzleClient->request( - 'GET', $this->url, [ + 'GET', + $this->url, + [ 'query' => [ 'operation' => 'describe', 'sessionName' => $sessionId, 'elementType' => $elementType ] - ]); + ] + ); } catch (GuzzleException $e) { throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); } @@ -621,5 +574,4 @@ protected function _processResponseError($processedData) throw VtigerError::init($this->vTigerErrors, 0, $processedData->error->message); } - } From 544b933e1c2713b915e6470eb4acc87f043e6f00 Mon Sep 17 00:00:00 2001 From: Clyde Cox Date: Wed, 27 Jun 2018 00:53:34 -0400 Subject: [PATCH 2/6] -Fix issue exception would be thrown instead of fetching new data --- src/Vtiger.php | 44 ++++++++++++++++---------------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/src/Vtiger.php b/src/Vtiger.php index 636f4c0..2551978 100644 --- a/src/Vtiger.php +++ b/src/Vtiger.php @@ -5,7 +5,6 @@ use GuzzleHttp\Exception\GuzzleException; use GuzzleHttp\Client; use InvalidArgumentException; -use Mockery\CountValidator\Exception; use Psr\Http\Message\ResponseInterface; use Config; use Cache; @@ -18,7 +17,6 @@ */ class Vtiger { - /** @var VtigerErrorElement[] */ private $vTigerErrors; @@ -104,16 +102,10 @@ protected function sessionId() // Get the sessionData from the cache $sessionData = json_decode(Cache::get('clystnet_vtiger')); - if (!$sessionData) { - throw VtigerError::init($this->vTigerErrors, 8, 'Could not get the session data from index clystnet_vtiger'); - } - if (isset($sessionData)) { - if ( - isset($sessionData) && + if (isset($sessionData) && property_exists($sessionData, 'expireTime') && - property_exists($sessionData, 'token') - ) { + property_exists($sessionData, 'token')) { if ($sessionData->expireTime < time() || empty($sessionData->token)) { $sessionData = $this->storeSession(); } @@ -172,13 +164,13 @@ protected function login($sessionData) } while (!isset($loginResult->success) && $tryCounter <= $this->maxRetries); if ($tryCounter >= $this->maxRetries) { - throw new VtigerError("Could not complete login request within " . $this->maxRetries . " tries", 5); + throw new VtigerError('Could not complete login request within ' . $this->maxRetries . ' tries', 5); } // If api login failed if ($response->getStatusCode() !== 200 || !$loginResult->success) { if (!$loginResult->success) { - if ($loginResult->error->code == "INVALID_USER_CREDENTIALS" || $loginResult->error->code == "INVALID_SESSIONID") { + if ($loginResult->error->code == 'INVALID_USER_CREDENTIALS' || $loginResult->error->code == 'INVALID_SESSIONID') { if (!Cache::has('clystnet_vtiger')) { throw VtigerError::init($this->vTigerErrors, 8, 'Nothing to delete for index clystnet_vtiger'); } else { @@ -194,7 +186,6 @@ protected function login($sessionData) // login ok so get sessionid and update our session $sessionId = $loginResult->result->sessionName; - if (Cache::has('clystnet_vtiger')) { $json = json_decode(Cache::pull('clystnet_vtiger')); $json->sessionid = $sessionId; @@ -219,9 +210,6 @@ protected function storeSession() $output = (object)$updated; $cacheResult = Cache::forever('clystnet_vtiger', json_encode($output)); - if (!$cacheResult) { - throw VtigerError::init($this->vTigerErrors, 8, 'Could not set the session data in index clystnet_vtiger'); - } return $output; } @@ -252,17 +240,17 @@ protected function getToken() } while (!isset($this->_processResponse($response)->success) && $tryCounter <= $this->maxRetries); if ($tryCounter >= $this->maxRetries) { - throw new VtigerError("Could not complete get token request within " . $this->maxRetries . " tries", 6); + throw new VtigerError('Could not complete get token request within ' . $this->maxRetries . ' tries', 6); } // decode the response $challenge = $this->_processResult($response); // Everything ok so create a token from response - $output = array( + $output = [ 'token' => $challenge->result->token, 'expireTime' => $challenge->result->expireTime, - ); + ]; return $output; } @@ -287,11 +275,11 @@ protected function close($sessionId) 'POST', $this->url, [ - 'query' => [ - 'operation' => 'logout', - 'sessionName' => $sessionId + 'query' => [ + 'operation' => 'logout', + 'sessionName' => $sessionId + ] ] - ] ); } catch (GuzzleException $e) { throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); @@ -484,12 +472,12 @@ public function describe($elementType) 'GET', $this->url, [ - 'query' => [ - 'operation' => 'describe', - 'sessionName' => $sessionId, - 'elementType' => $elementType + 'query' => [ + 'operation' => 'describe', + 'sessionName' => $sessionId, + 'elementType' => $elementType + ] ] - ] ); } catch (GuzzleException $e) { throw VtigerError::init($this->vTigerErrors, 7, $e->getMessage()); From e7a7eb008bff44ed30e827d5577c4b8fca45123d Mon Sep 17 00:00:00 2001 From: Clyde Cox Date: Wed, 27 Jun 2018 04:32:52 -0400 Subject: [PATCH 3/6] -Add query builder around query function, update readme --- README.md | 26 ++++++++++++++++++++++---- src/Vtiger.php | 23 +++++++++++++++++++++++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 3d1e2cd..6753322 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,11 @@ Use the Vtiger webservice (REST) API from within Laravel for the following opera - Retrieve - Update - Delete +- Search - Query - Describe -See [Third Party App Integration (REST APIs)](http://community.vtiger.com/help/vtigercrm/developers/third-party-app-integration.html) +See [Third Party App Integration (REST APIs)](http://community.vtiger.com/help/vtigercrm/developers/third-party-app-integration.html) ## Installation, Configuration and Usage @@ -46,7 +47,6 @@ See [Third Party App Integration (REST APIs)](http://community.vtiger.com/help/v - In your application, edit *config/vtiger.php* and replace the following array values - Set the url to the https://{DOMAIN_NAME}/webservice.php - Set the username and accesskey with your CRM username and access key. - - Set the session drive to either file or reddis - Set persistconnection to false if you want a fresh login with each request |key |value | @@ -54,7 +54,6 @@ See [Third Party App Integration (REST APIs)](http://community.vtiger.com/help/v |url |http://www.example.com/webservice.php| |username |API | |accesskey |irGsy9HB0YOZdEA | - |sessiondriver |file | |persistconnection|true | |max_retries |10 | @@ -70,7 +69,7 @@ use Vtiger; #### Create -To insert a record into the CRM, first create an array of data to insert. Don't forget the added the id of the `assigned_user_id` (i.e. '4x12') otherwise the insert will fail as `assigned_user_id` is a mandatory field. +To insert a record into the CRM, first create an array of data to insert. Don't forget the added the id of the `assigned_user_id` (i.e. '4x12') otherwise the insert will fail as `assigned_user_id` is a mandatory field. ```php $data = array( 'assigned_user_id' => '', @@ -122,6 +121,25 @@ $obj = Vtiger::retrieve($id); var_dump($obj); ``` +#### Search + +This function is a sql query builder wrapped around the query function. +```php +$search = []; +$search[] = [ + 'firstname' => ['=', 'John'], + 'lastname' => ['=', 'Smith'], + 'login_date' => ['>=', '00-00-00 00:00:00'], +]; + +$obj = Vtiger::search('Leads', $search); + +//loop over result +foreach($obj->result as $result) { + // do something +} +``` + #### Query To use the [Query Operation](http://community.vtiger.com/help/vtigercrm/developers/third-party-app-integration.html#query-operation), you first need to create a SQL query. diff --git a/src/Vtiger.php b/src/Vtiger.php index 2551978..949b261 100644 --- a/src/Vtiger.php +++ b/src/Vtiger.php @@ -8,6 +8,7 @@ use Psr\Http\Message\ResponseInterface; use Config; use Cache; +use DB; /** * Laravel wrapper for the VTgier API @@ -57,6 +58,7 @@ public function __construct() 1 => new VtigerErrorElement('API request did not complete correctly - Response code: ', 1), 2 => new VtigerErrorElement('Success property not set on VTiger response', 2), 3 => new VtigerErrorElement('Error property not set on VTiger response when success is false', 3), + 4 => new VtigerErrorElement('There are no search fields in the array', 4), 5 => new VtigerErrorElement('Could not complete login request within ' . $this->maxRetries . ' tries', 5), 6 => new VtigerErrorElement( 'Could not complete get token request within ' . $this->maxRetries . ' tries', @@ -318,6 +320,27 @@ public function query($query) return $this->_processResult($response); } + public function search($module, $array) + { + if (empty($array) || !$module) { + throw VtigerError::init($this->vTigerErrors, 4); + } + + $query = DB::table($module); + foreach ($array as $item) { + $query->where(key($item), $item[key($item)][0], $item[key($item)][1]); + } + + $bindings = $query->getBindings(); + $queryString = $query->toSQL(); + + foreach ($bindings as $binding) { + $queryString = preg_replace('/\?/', DB::connection()->getPdo()->quote($binding), $queryString, 1); + } + + return $this->query($queryString); + } + /** * Retreive a record from the VTiger API * Format of id must be {moudler_code}x{item_id}, e.g 4x12 From 78feb6bab15e4c0d109df516c1032f90e638b7e0 Mon Sep 17 00:00:00 2001 From: Clyde Cox Date: Wed, 27 Jun 2018 06:25:42 -0400 Subject: [PATCH 4/6] -Update to search function, now accepts type of query builder --- README.md | 17 +++++++++-------- src/Vtiger.php | 20 ++++++++------------ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 6753322..65550b2 100644 --- a/README.md +++ b/README.md @@ -123,16 +123,11 @@ var_dump($obj); #### Search -This function is a sql query builder wrapped around the query function. +This function is a sql query builder wrapped around the query function. Accepts instance of laravels QueryBuilder. ```php -$search = []; -$search[] = [ - 'firstname' => ['=', 'John'], - 'lastname' => ['=', 'Smith'], - 'login_date' => ['>=', '00-00-00 00:00:00'], -]; +$query = DB::table('Leads')->select('id', 'firstname', 'lastname')->where('firstname', 'John'); -$obj = Vtiger::search('Leads', $search); +$obj = Vtiger::search('Leads', $query); //loop over result foreach($obj->result as $result) { @@ -140,6 +135,12 @@ foreach($obj->result as $result) { } ``` +By default the function will quote but not escape your inputs, if you wish for your data to not be quoted, set the 3rd paramater to false like so: +```php +$obj = Vtiger::search('Leads', $query, false); +``` + +Also keep in mind that Victiger has several limitations on it's sql query capabilities. You can not use conditional grouping i.e "where (firstname = 'John' AND 'lastname = 'Doe') OR (firstname = 'Jane' AND lastname = 'Smith') will fail. #### Query To use the [Query Operation](http://community.vtiger.com/help/vtigercrm/developers/third-party-app-integration.html#query-operation), you first need to create a SQL query. diff --git a/src/Vtiger.php b/src/Vtiger.php index 949b261..67416c1 100644 --- a/src/Vtiger.php +++ b/src/Vtiger.php @@ -320,23 +320,19 @@ public function query($query) return $this->_processResult($response); } - public function search($module, $array) + public function search($query, $quote = true) { - if (empty($array) || !$module) { - throw VtigerError::init($this->vTigerErrors, 4); - } - - $query = DB::table($module); - foreach ($array as $item) { - $query->where(key($item), $item[key($item)][0], $item[key($item)][1]); - } - $bindings = $query->getBindings(); $queryString = $query->toSQL(); foreach ($bindings as $binding) { - $queryString = preg_replace('/\?/', DB::connection()->getPdo()->quote($binding), $queryString, 1); + if ($quote) { + $queryString = preg_replace('/\?/', DB::connection()->getPdo()->quote($binding), $queryString, 1); + } else { + $queryString = preg_replace('/\?/', $binding, $queryString, 1); + } } + $queryString = str_replace('`', '', $queryString) . ';'; return $this->query($queryString); } @@ -582,7 +578,7 @@ protected function _processResponseError($processedData) if (!isset($processedData->error)) { throw VtigerError::init($this->vTigerErrors, 3); } - + dd($processedData); throw VtigerError::init($this->vTigerErrors, 0, $processedData->error->message); } } From 8bf37e96e8c868a419cd5ae91e94606b6434f283 Mon Sep 17 00:00:00 2001 From: Clyde Cox Date: Wed, 27 Jun 2018 18:59:31 -0400 Subject: [PATCH 5/6] -Fix spelling in cache key name --- src/Vtiger.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Vtiger.php b/src/Vtiger.php index 67416c1..8a7c45a 100644 --- a/src/Vtiger.php +++ b/src/Vtiger.php @@ -59,7 +59,7 @@ public function __construct() 2 => new VtigerErrorElement('Success property not set on VTiger response', 2), 3 => new VtigerErrorElement('Error property not set on VTiger response when success is false', 3), 4 => new VtigerErrorElement('There are no search fields in the array', 4), - 5 => new VtigerErrorElement('Could not complete login request within ' . $this->maxRetries . ' tries', 5), + 5 => new VtigerErrorElement('Could not complete login request within ' . $this->maxRetries . ' tries', 5), 6 => new VtigerErrorElement( 'Could not complete get token request within ' . $this->maxRetries . ' tries', 6 @@ -191,7 +191,7 @@ protected function login($sessionData) if (Cache::has('clystnet_vtiger')) { $json = json_decode(Cache::pull('clystnet_vtiger')); $json->sessionid = $sessionId; - Cache::forever('clystnetVtiger', json_encode($json)); + Cache::forever('clystnet_vtiger', json_encode($json)); } else { throw VtigerError::init($this->vTigerErrors, 8, 'There is no key for index clystnet_vtiger.'); } From 5678372fdf1013f7dddbb3490519b7cb57a9258f Mon Sep 17 00:00:00 2001 From: Clyde Cox Date: Mon, 9 Jul 2018 09:41:48 -0400 Subject: [PATCH 6/6] -Remove dd() left over from debugging --- src/Vtiger.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Vtiger.php b/src/Vtiger.php index 8a7c45a..b623f0e 100644 --- a/src/Vtiger.php +++ b/src/Vtiger.php @@ -578,7 +578,6 @@ protected function _processResponseError($processedData) if (!isset($processedData->error)) { throw VtigerError::init($this->vTigerErrors, 3); } - dd($processedData); throw VtigerError::init($this->vTigerErrors, 0, $processedData->error->message); } }