From 5036126c558772f85bf54a661e045e4facbf3eca Mon Sep 17 00:00:00 2001 From: Amr Ahmed Date: Wed, 10 Jun 2020 19:41:13 +0200 Subject: [PATCH] Added New Zoho Polymorphism model to reused across laravel project models, that holds the zoho id inside local database. --- database/migrations/.gitignore | 2 - .../2020_06_04_000001_create_zohos_table.php | 33 +++ .../Repositories/ZohoableRepository.php | 22 ++ src/CriteriaBuilder.php | 65 +++++ src/Exceptions/InvalidZohoable.php | 31 +++ src/Models/Zoho.php | 25 ++ src/Providers/ZohoServiceProvider.php | 8 +- src/RestClient.php | 2 +- src/Traits/Zohoable.php | 228 ++++++++++++++++++ src/ZohoModule.php | 63 ++--- src/ZohoOrganization.php | 2 +- src/Zohoable.php | 22 ++ tests/Integration/IntegrationTestCase.php | 2 +- tests/Integration/ZohoModuleTest.php | 43 ++-- tests/Integration/ZohoOrganizationTest.php | 8 +- 15 files changed, 484 insertions(+), 72 deletions(-) delete mode 100644 database/migrations/.gitignore create mode 100644 database/migrations/2020_06_04_000001_create_zohos_table.php create mode 100644 src/Contracts/Repositories/ZohoableRepository.php create mode 100644 src/CriteriaBuilder.php create mode 100644 src/Exceptions/InvalidZohoable.php create mode 100644 src/Models/Zoho.php create mode 100644 src/Traits/Zohoable.php create mode 100644 src/Zohoable.php diff --git a/database/migrations/.gitignore b/database/migrations/.gitignore deleted file mode 100644 index c96a04f..0000000 --- a/database/migrations/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -* -!.gitignore \ No newline at end of file diff --git a/database/migrations/2020_06_04_000001_create_zohos_table.php b/database/migrations/2020_06_04_000001_create_zohos_table.php new file mode 100644 index 0000000..f46e496 --- /dev/null +++ b/database/migrations/2020_06_04_000001_create_zohos_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('zoho_id')->unique()->index(); + $table->morphs('zohoable'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('personal_access_tokens'); + } +} diff --git a/src/Contracts/Repositories/ZohoableRepository.php b/src/Contracts/Repositories/ZohoableRepository.php new file mode 100644 index 0000000..812c472 --- /dev/null +++ b/src/Contracts/Repositories/ZohoableRepository.php @@ -0,0 +1,22 @@ +criteria = ""; + + $builder->criteria .= static::queryBuilder($field, $operator, $value); + + return $builder; + } + + public function startsWith($field, $value, $operator = 'starts_with') + { + $this->criteria .= ' and ' . $this->queryBuilder($field, $operator, $value); + + return $this; + } + + public function andWhere($field, $value, $operator = 'equals') + { + $this->criteria .= ' and ' . $this->queryBuilder($field, $operator, $value); + + return $this; + } + + public function orWhere($field, $value, $operator = 'equals') + { + $this->criteria .= ' or ' . $this->queryBuilder($field, $operator, $value); + + return $this; + } + + private static function queryBuilder(...$args) + { + return "($args[0]:$args[1]:$args[2])"; + } + + public function toString() + { + return $this->getCriteria() ?? ''; + } + + public function getCriteria() + { + return $this->criteria; + } +} diff --git a/src/Exceptions/InvalidZohoable.php b/src/Exceptions/InvalidZohoable.php new file mode 100644 index 0000000..efef632 --- /dev/null +++ b/src/Exceptions/InvalidZohoable.php @@ -0,0 +1,31 @@ +zohoId()}"); + } +} diff --git a/src/Models/Zoho.php b/src/Models/Zoho.php new file mode 100644 index 0000000..468b1a1 --- /dev/null +++ b/src/Models/Zoho.php @@ -0,0 +1,25 @@ +morphTo(); + } +} diff --git a/src/Providers/ZohoServiceProvider.php b/src/Providers/ZohoServiceProvider.php index 6e02951..7bc59ff 100644 --- a/src/Providers/ZohoServiceProvider.php +++ b/src/Providers/ZohoServiceProvider.php @@ -95,10 +95,10 @@ protected function registerPublishing() __DIR__ . '/../../config/zoho.php' => $this->app->configPath('zoho.php'), ], 'zoho-config'); -// $this->publishes([ -// __DIR__ . '/../../database/migrations' => $this->app->databasePath('migrations'), -// ], 'zoho-migrations'); -// + $this->publishes([ + __DIR__ . '/../../database/migrations' => $this->app->databasePath('migrations'), + ], 'zoho-migrations'); + // $this->publishes([ // __DIR__ . '/../../resources/views' => $this->app->resourcePath('views/vendor/zoho'), // ], 'zoho-views'); diff --git a/src/RestClient.php b/src/RestClient.php index 8205449..ee11d03 100644 --- a/src/RestClient.php +++ b/src/RestClient.php @@ -29,7 +29,7 @@ public function __construct($rest) public function getAllModules() { - return $this->rest->getAllModules(); + return $this->rest->getAllModules()->getData(); } public function useModule($module_api_name = 'leads') diff --git a/src/Traits/Zohoable.php b/src/Traits/Zohoable.php new file mode 100644 index 0000000..0ac6b24 --- /dev/null +++ b/src/Traits/Zohoable.php @@ -0,0 +1,228 @@ +morphOne(ZohoModel::class, 'zohoable'); + } + + /** + * Retrieve the Zoho ID for current model item. + * + * @return string|null + */ + public function zohoId() + { + if (!$this->zoho) return null; + + return $this->zoho->zoho_id; + } + + /** + * Determine if the entity has a Zoho ID. + * + * @return bool + */ + public function hasZohoId() + { + return !is_null($this->zohoId()); + } + + /** + * create or update the current model with zoho id + * + * @param null $id + * @return mixed + * @throws Exception + */ + public function createOrUpdateZohoId($id = null) + { + try { + return $this->createZohoId($id); + } catch (InvalidZohoable $e) { + try { + return $this->updateZohoId($id); + } catch (InvalidZohoable $e) { + throw new Exception('something went wrong!'); + } + } + } + + /** + * Update current zoho id for this model + * + * @param null $id + * @return mixed + * @throws InvalidZohoable + */ + public function updateZohoId($id = null) + { + if (!$this->hasZohoId()) { + throw InvalidZohoable::nonZohoable($this); + } + + if (!$id) { + $id = $this->findByCriteria()->getEntityId(); + } + + $this->zoho()->update(['zoho_id' => $id]); + return $this->load('zoho'); + } + + /** + * create zoho id from $id or search on zoho by criteria + * + * @param null $id + * @return mixed + * @throws InvalidZohoable + */ + public function createZohoId($id = null) + { + if ($this->hasZohoId()) { + throw InvalidZohoable::exists($this); + } + + if (!$id) { + $id = $this->findByCriteria()->getEntityId(); + } + + $this->zoho()->create(['zoho_id' => $id]); + return $this->load('zoho'); + } + + /** + * delete zoho id from this model + * + * @return mixed + * @throws InvalidZohoable + */ + public function deleteZohoId() + { + if (!$this->hasZohoId()) { + throw InvalidZohoable::nonZohoable($this); + } + + $this->zoho()->delete(); + return $this->load('zoho'); + } + + protected function findByCriteria() + { + if ($this->searchCriteria() == '') return null; + + return last( + $this->zoho_module->searchRecordsByCriteria( + $this->searchCriteria() + ) + ); + } + + /** + * Create a Tap customer for the given model. + * + * @param array $options + * @return object + * @throws InvalidZohoable + */ + public function createAsZohoable(array $options = []) + { + if ($this->zohoId()) { + throw InvalidZohoable::exists($this); + } + + $options = array_merge($this->zohoMandatoryFields(), $options); + + // Here we will create the ZCRMRecord instance on Zoho and store the ID of the + // record from Zoho. This ID will correspond with the Zoho record instance + // and allow us to retrieve records from Zoho later when we need to work. + $record = $this->zoho_module->create($options); + + $this->createZohoId($record->getEntityId()); + + return $record; + } + + /** + * Find and delete zoho record and remove zoho_id from model + * + * @return mixed + * @throws InvalidZohoable + * @throws ZCRMException + */ + public function deleteZohoable() + { + $this->asZohoObject()->delete(); + return $this->deleteZohoId(); + } + + /** + * Get the zoho module name associated with the model. + * + * @return string + */ + public function getZohoModuleName() + { + return $this->zoho_module_name ?? Str::snake(Str::pluralStudly(class_basename($this))); + } + + /** + * Get the Zoho Module for this model + * + * @return ZohoModule + */ + public function getZohoModule() + { + return ZohoManager::useModule($this->getZohoModuleName()); + } + + /** + * get zoho object by the module id + * + * @return object|ZCRMRecord + */ + public function asZohoObject() + { + return $this->findByZohoId($this->zohoId()); + } + + public function findByZohoEmail($email) + { + return last($this->zoho_module->searchRecordsByEmail($email)); + } + + /** + * find record by it's ID + * + * @param $id + * @return object|ZCRMRecord + */ + public function findByZohoId($id) + { + return $this->zoho_module->getRecord($id); + } + + /** + * Determine if the entity has a Zoho ID and throw an exception if not. + * + * @return void + * @throws InvalidZohoable + */ + protected function assertZohoableExists() + { + if (!$this->zohoId()) { + throw InvalidZohoable::nonZohoable($this); + } + } +} diff --git a/src/ZohoModule.php b/src/ZohoModule.php index 954913a..bf2345c 100644 --- a/src/ZohoModule.php +++ b/src/ZohoModule.php @@ -11,7 +11,6 @@ class ZohoModule protected $rest; protected $module_api_name; protected $moduleIns; - protected $criteria = ""; protected $operators = ['equals', 'starts_with']; /** @@ -139,7 +138,7 @@ public function searchRecordsByEmail($email = '', $page = 1, $perPage = 200) * @param int $perPage //To get the list of records available per page. Default value for per page is 200. * @return ZCRMRecord[] */ - public function searchRecordsByCriteria($criteria = "", $page = 1, $perPage = 200) + public function searchRecordsByCriteria($criteria, $page = 1, $perPage = 200) { $param_map = ["page" => $page, "per_page" => $perPage]; return $this->moduleIns->searchRecordsByCriteria($criteria, $param_map)->getData(); @@ -159,6 +158,23 @@ public function insert($record) return $this->moduleIns->createRecords($records)->getData(); } + /** + * create record instance that contains the array keys and values + * + * @param array $args + * @return object + */ + public function create($args = []) + { + $record = $this->getRecordInstance(); + + foreach ($args as $key => $value) { + $record->setFieldValue($key, $value); + } + + return $record->create()->getData(); + } + /** * update existing entities in the module. * @@ -174,52 +190,17 @@ public function update($record) } /** + * @param CriteriaBuilder $builder * @param int $page //to get the list of records from the respective pages. Default value for page is 1. * @param int $perPage //To get the list of records available per page. Default value for per page is 200. * @return ZCRMRecord[] */ - public function search($page = 1, $perPage = 200) + public function search($builder, $page = 1, $perPage = 200) { - if ($this->criteria !== "") { - return $this->searchRecordsByCriteria($this->criteria, $page, $perPage); + if ($builder->toString() !== "") { + return $this->searchRecordsByCriteria($builder->toString(), $page, $perPage); } return null; } - - /** - * add criteria to the search - * - * @param $field - * @param $value - * @param string $operator - * @return $this - */ - public function where($field, $value, $operator = 'equals') - { - $this->criteria = ""; - - $this->criteria .= $this->queryBuilder($field, $operator, $value); - - return $this; - } - - public function andWhere($field, $value, $operator = 'equals') - { - $this->criteria .= ' and ' . $this->queryBuilder($field, $operator, $value); - - return $this; - } - - public function orWhere($field, $value, $operator = 'equals') - { - $this->criteria .= ' or ' . $this->queryBuilder($field, $operator, $value); - - return $this; - } - - private function queryBuilder(...$args) - { - return "($args[0]:$args[1]:$args[2])"; - } } diff --git a/src/ZohoOrganization.php b/src/ZohoOrganization.php index edd54f3..63867ce 100644 --- a/src/ZohoOrganization.php +++ b/src/ZohoOrganization.php @@ -28,7 +28,7 @@ public function __construct($rest) */ public function getOrganizationDetails() { - return $this->rest->getOrganizationDetails()->getData(); + return $this->rest->getOrganizationInstance(); } /** diff --git a/src/Zohoable.php b/src/Zohoable.php new file mode 100644 index 0000000..5419311 --- /dev/null +++ b/src/Zohoable.php @@ -0,0 +1,22 @@ +zoho_module = $this->getZohoModule(); + } +} diff --git a/tests/Integration/IntegrationTestCase.php b/tests/Integration/IntegrationTestCase.php index 1b01bbb..2d9c6b0 100644 --- a/tests/Integration/IntegrationTestCase.php +++ b/tests/Integration/IntegrationTestCase.php @@ -20,7 +20,7 @@ public static function setUpBeforeClass(): void 'applicationLogFilePath' => './tests/Fixture/Storage/oauth/logs', 'token_persistence_path' => './tests/Fixture/Storage/oauth/tokens', 'accounts_url' => 'https://accounts.zoho.com', - 'sandbox' => false, + 'sandbox' => true, 'apiBaseUrl' => 'www.zohoapis.com', 'apiVersion' => 'v2', 'access_type' => 'offline', diff --git a/tests/Integration/ZohoModuleTest.php b/tests/Integration/ZohoModuleTest.php index 03f26ce..6c7ad28 100644 --- a/tests/Integration/ZohoModuleTest.php +++ b/tests/Integration/ZohoModuleTest.php @@ -1,14 +1,16 @@ client = $this->getClient(); - $this->module = $this->client->useModule('leads'); + $this->module = $this->client->useModule('Leads'); } /** @test */ @@ -33,16 +35,16 @@ public function is_can_get_module_by_name() { $leads = $this->client->useModule(); - self::assertInstanceOf(ZCRMModule::class, $leads); + self::assertInstanceOf(ZohoModule::class, $leads); } /** @test */ public function it_can_instantiate_a_record_with_id() { - $record = $this->module->getRecordInstance('3582074000005414003'); + $record = $this->module->getRecordInstance(self::LEAD_ID_FOR_TEST); self::assertInstanceOf(ZCRMRecord::class, $record); - self::assertEquals($record->getModuleApiName(), 'leads'); - self::assertEquals($record->getEntityId(), '3582074000005414003'); + self::assertEquals('Leads', $record->getModuleApiName()); + self::assertEquals(self::LEAD_ID_FOR_TEST, $record->getEntityId()); } /** @test */ @@ -50,7 +52,7 @@ public function it_can_instantiate_a_module_with_api_name() { $module = $this->module->getModuleInstance(); self::assertInstanceOf(ZCRMModule::class, $module); - self::assertEquals($module->getAPIName(), 'leads'); + self::assertEquals('Leads', $module->getAPIName()); } /** @test */ @@ -63,17 +65,17 @@ public function it_can_get_records_for_given_module_api_name() /** @test */ public function it_can_get_record_by_module_api_name_and_record_id() { - $record = $this->module->getRecord('3582074000005414003'); + $record = $this->module->getRecord(self::LEAD_ID_FOR_TEST); self::assertInstanceOf(ZCRMRecord::class, $record); } /** @test */ public function it_can_search_for_word_on_specific_module() { - $records = $this->module->searchRecordsByWord('mohamed'); + $records = $this->module->searchRecordsByWord('Amr Ahmed'); - self::assertInstanceOf(ZCRMRecord::class, $records[0]); - self::assertEquals('3582074000000655089', $records[0]->getOwner()->getId()); + self::assertInstanceOf(ZCRMRecord::class, end($records)); + self::assertEquals(self::LEAD_ID_FOR_TEST, end($records)->getEntityId()); } /** @test */ @@ -95,7 +97,8 @@ public function it_can_search_for_email_on_specific_module() } /** @test */ - public function it_can_search_by_criteria() { + public function it_can_search_by_criteria() + { $records = $this->module->searchRecordsByCriteria("(City:equals:Al Wasitah) and (State:equals:Al Fayyum)"); self::assertInstanceOf(ZCRMRecord::class, $records[0]); @@ -103,7 +106,8 @@ public function it_can_search_by_criteria() { } /** @test */ - public function it_can_search_by_field_name() { + public function it_can_search_by_field_name() + { $records = $this->module->where('City', 'Al Wasitah')->search(); self::assertInstanceOf(ZCRMRecord::class, $records[0]); @@ -111,7 +115,8 @@ public function it_can_search_by_field_name() { } /** @test */ - public function it_can_search_with_multiple_criteria() { + public function it_can_search_with_multiple_criteria() + { $records = $this->module ->where('City', 'Al Wasitah') ->andWhere('State', 'Al Fayyum') @@ -121,7 +126,9 @@ public function it_can_search_with_multiple_criteria() { self::assertEquals('falah.alhajeri6999@hotmail.com', $records[0]->getFieldValue('Email')); } - /** @test */ + /** @test + * @throws ZCRMException + */ public function it_can_create_new_record() { $lead = $this->module->getRecordInstance(); @@ -141,7 +148,9 @@ public function it_can_create_new_record() $lead->delete(); } - /** @test */ + /** @test + * @throws ZCRMException + */ public function it_can_update_records() { $lead = $this->module->getRecord('3582074000002383003'); diff --git a/tests/Integration/ZohoOrganizationTest.php b/tests/Integration/ZohoOrganizationTest.php index e1ded3e..eaa7682 100644 --- a/tests/Integration/ZohoOrganizationTest.php +++ b/tests/Integration/ZohoOrganizationTest.php @@ -1,21 +1,19 @@ client = $this->getClient(); - $this->org = $this->client->currentOrg(); + $client = $this->getClient(); + $this->org = $client->currentOrg(); } /** @test */