init(); $this->manager = $manager; $this->setSettings($settings); $this->onInit(); } /** * Init event * @return mixed|void */ protected function onInit() { } /** * Service unique key * @return string */ abstract public function getKey(): string; /** * Forward calls to manager * @param string $method * @param array $parameters * @return mixed */ public function __call($method, $parameters) { return $this->forwardCallTo($this->manager, $method, $parameters); } /** * Set service settings * @param array|string $keys * @param mixed $value * @param array $opts * @return mixed|void */ public function setSettings($keys, $value = false, array $opts = []) { if (is_array($keys)) { foreach ($keys as $k => $v) { $this->settings[$k] = $v; } } else { $this->settings[$keys] = $value; } } /** * Get settings * @param string|string[] $keys * @param mixed $default * @return array|mixed */ public function getSettings($keys, $default = null) { if (is_array($keys)) { $res = []; foreach ($keys as $key) { if (isset($this->settings[$key])) { $res[$key] = $this->settings[$key]; } } return $res; } elseif (is_string($keys)) { if (isset($this->settings[$keys])) { return $this->settings[$keys]; } if (isset($this->settings['settings'][$keys])) { return $this->settings['settings'][$keys]; } return $default; } return $this->settings; } /** * Update user balance after activation * @param bool $update * @return mixed|void */ public function updateBalance(?bool $update = null) { if (! is_null($update)) { $this->updateBalance = $update; } return $this->updateBalance; } /** * Set activation settings * @param mixed $itemID * @param mixed $userID * @param array|null $settings * @return mixed|void */ public function setActivationSettings($itemID, $userID, ?array $settings = null) { $this->itemID = $itemID; $this->userID = $userID; if ($this->itemID) { $exist = $this->getItemServiceData($this->getKey(), $this->itemID); if (! empty($exist['service_status'])) { $this->status = $exist['service_status']; } if (! empty($exist['settings'])) { $this->activationSettings = array_merge($this->activationSettings ?? [], $exist['settings']); } if (isset($exist['expires'])) { $this->expires = $exist['expires']; } } if (! is_null($settings)) { unset($settings['group'], $settings['key'], $settings['user'], $settings['item']); $this->activationSettings = array_merge($this->activationSettings ?? [], $settings); } } /** * Get activation settings * @return array */ public function getActivationSettings() { $data = [ 'group' => $this->getGroupKey(), 'key' => $this->getKey(), 'user' => $this->userID, 'item' => $this->itemID, ]; if (! empty($this->activationSettings)) { $data = array_merge($data, $this->activationSettings); } return $data; } /** * Set activation pack * @param Service|null $pack * @return mixed|void */ public function setActivationPack(?Service $pack) { $this->activationPack = $pack; } /** * Service can be in a pack * @return bool */ public function isPackable(): bool { return false; } /** * Service is a pack * @return bool */ public function isPack(): bool { return ($this->getType() === static::TYPE_SERVICEPACK); } /** * Service type * @return int */ public function getType(): int { return (int)($this->settings['service_type'] ?? static::TYPE_SERVICE); } /** * Service group key * @return string */ public function getGroupKey(): string { return $this->manager->getGroupKey(); } /** * Service title * @param string|null $lang * @return string */ public function getTitle(?string $lang = null): string { if (isset($this->settings['title'])) { if (is_string($this->settings['title'])) { return $this->settings['title']; } elseif (is_array($this->settings['title'])) { return $this->locale->value($this->settings['title'], $lang); } } return ''; } /** * Service enabled * @return bool */ public function isEnabled(): bool { return !empty($this->settings['enabled']); } /** * Service allowed * @return bool */ public function isAllowed() { return true; } /** * Service is visible in services list and has settings * @return bool */ public function isEditable() { return true; } /** * Calculate price * @return float */ protected function calculatePrice(): float { return (float)($this->settings['price'] ?? 0); } /** * Service price * @return float */ public function getPrice(): float { return $this->app->filter('svc.service.price', $this->calculatePrice(), $this); } /** * Form forwarding parameters * @param array $params */ public function formParams(array &$params = []) { } /** * Service cost (in activate process) * @return float */ public function getCost(): float { return $this->getPrice(); } /** * Service expiration date or 0 - unlimited * @return int */ public function getExpires(): int { return 0; } /** * Service bill description * @return string */ abstract public function getBillDescription(): string; /** * Activate service * @param int|null $expires * @param array $opts * @return bool */ public function activate(?int $expires = null, array $opts = []): bool { $success = $this->onActivateBefore(); if ($success) { $success = $this->activateItemService( $this->getKey(), $this->itemID, $this->userID, $this->activationSettings, $expires ?? $this->getExpires(), $opts ); } if ($success) { $this->status = Service::STATUS_ACTIVE; $this->onActivateAfter(); if (is_null($this->activationPack)) { $billID = $this->manager->createBill($this, $this->updateBalance); if ($billID) { $this->onBillCreated($billID); } } } $this->onActivateFinish($success); return $success; } /** * Before activation event * @return bool */ public function onActivateBefore() { return true; } /** * After activation event * @return mixed|void */ public function onActivateAfter() { } /** * After create bill * @param integer $billID * @return void */ public function onBillCreated($billID) { } /** * Finish Activation event * @param integer $success * @return void */ public function onActivateFinish($success) { } /** * Prepare activation form data * @param array $data @ref * @param array $opts * @return void */ public function onActivationForm(array &$data, array $opts = []) { $exist = $this->getItemServiceData($this->getKey(), $this->itemID); if (empty($exist)) { return; } $exist = $exist->toArray(); $this->status = $exist['service_status']; if ($exist['service_status'] != static::STATUS_ACTIVE) { return; } if (! empty($exist['expires']) && strtotime($exist['expires']) > time()) { $data['expire'] = $exist['expires']; } if (! empty($exist['settings'])) { $this->activationSettings = array_merge($this->activationSettings ?? [], $exist['settings']); } } /** * Deactivate service * @param array $opts * @return bool */ public function deactivate(array $opts = []): bool { $success = $this->deactivateItemService($this->getKey(), $this->itemID, $opts); if ($success) { $this->onDeactivate(); $this->getServiceStatusModel()->where([ 'group_key' => $this->getGroupKey(), 'service_key' => $this->getKey(), 'item_id' => $this->itemID, ])->delete(); } return $success; } /** * After deactivation event * @return mixed|void */ public function onDeactivate() { } /** * Cron event * @return mixed|void */ public function onCron() { if (! $this->isCron()) { return; } $this->getServiceStatusModel()->tap(function ($query) { /** @var $query ServiceStatus */ $query ->where('group_key', $this->getGroupKey()) ->where('service_key', $this->getKey()) ->where('service_status', static::STATUS_ACTIVE) ->where('expires', '<', date('Y-m-d H:i:s')) ->where('expires', '>', 0) ->chunkById(50, function ($items) { foreach ($items as $item) { $this->setActivationSettings($item->item_id, 0, $item->settings ?? []); $this->deactivate(); } }) ; }); } /** * Admin settings form * @param mixed $form * @return mixed|void */ public function onAdminSettings($form) { } /** * Get service install settings * @return array */ public function getInstallSettings(): array { return []; } /** * Install service event * @param array $opts * @return mixed|void */ public function onInstall(array $opts = []) { } /** * Uninstall service event * @param array $opts * @return mixed|void */ public function onUninstall(array $opts = []) { } /** * Enable service event */ public function onEnable() { } /** * Disable service event */ public function onDisable() { } }