workflowNode = $workflowNode; $this->em = $this->workflowNode->getEntityManager(); } /** * Route contract and execute requests according to meta. * @param string $name * @param array $arguments * @throws \Exception * @return \Workspace\Contract\AbstractBase|array */ public function __call($name, $arguments) { #-> Is this a contract, execution or direct execution request? if ('contractRoute' == substr($name, 0, 13)) { #-> Route Contract request, establish action and retrieve meta. $action = substr($name, 13); $meta = 'metaRoute' . $action; if (!isset($this->$meta)) { throw new \Exception('contractRoute:Requested DataBin routing meta structure not defined.'); } $meta = $this->$meta; if (isset($meta['Flags']) && is_array($meta['Flags'])) { foreach ($meta['Flags'] as $flag => $value) { \Utility\Registry::setOnce($flag, $value); } } return $this->conRoute($meta, $arguments[0], $arguments[1]); } elseif ('executeRoute' == substr($name, 0, 12)) { #-> Route Execution request, establish action and retrieve meta. $action = substr($name, 12); $meta = 'metaRoute' . $action; $meta = $this->$meta; if (isset($meta['Flags']) && is_array($meta['Flags'])) { foreach ($meta['Flags'] as $flag => $value) { \Utility\Registry::setOnce($flag, $value); } } return $this->exeRoute($meta, $arguments[0], $arguments[1], $arguments[2]); } elseif ('contract' == substr($name, 0, 8)) { #-> Task Contract request, establish action and retrieve meta. $action = substr($name, 8); $meta = 'meta' . $action; if (!isset($this->$meta)) { throw new \Exception('contract:Requested DataBin meta structure not defined.'); } $meta = $this->$meta; if (isset($meta['Flags']) && is_array($meta['Flags'])) { foreach ($meta['Flags'] as $flag => $value) { \Utility\Registry::setOnce($flag, $value); } } $hardLink = array( 'List', 'SelectList', 'Grid', 'View', 'Create', 'Update', 'Delete' ); if (in_array($action, array( 'List', 'SelectList', 'Grid', 'View', 'Create', 'Update', 'Delete' ))) { $meta['Type'] = $action; } if (!isset($meta['Type'])) { throw new \Exception( 'DataBin meta structure provided without handling method and no core Type.' ); } switch ($meta['Type']) { case self::ACTION_TYPE_SESSION: return $this->conSession($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_LIST: return $this->conList($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_SELECT_LIST: return $this->conSelectList($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_GRID: return $this->conGrid($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_VIEW: return $this->conView($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_CREATE: return $this->conCreate($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_UPDATE: return $this->conUpdate($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_DELETE: return $this->conDelete($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_UNDELETE: return $this->conUnDelete($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_PDF: return $this->conPdf($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_REPORT: return $this->conReport($meta, $arguments[0], $arguments[1]); break; default: throw new \Exception( 'Undefined core Type specified in DataBin meta structure.' ); break; } } elseif ('execute' == substr($name, 0, 7)) { #-> Task Execution request, establish action and retrieve meta. $action = substr($name, 7); $meta = 'meta' . $action; $meta = $this->$meta; if (isset($meta['Flags']) && is_array($meta['Flags'])) { foreach ($meta['Flags'] as $flag => $value) { \Utility\Registry::setOnce($flag, $value); } } switch ($meta['Type']) { case self::ACTION_TYPE_SESSION: return $this->exeSession($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_LIST: return $this->exeList($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_SELECT_LIST: return $this->exeSelectList($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_GRID: return $this->exeGrid($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_VIEW: return $this->exeView($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_CREATE: return $this->exeCreate($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_UPDATE: return $this->exeUpdate($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_DELETE: return $this->exeDelete($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_UNDELETE: return $this->exeUnDelete($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_PDF: return $this->exePdf($meta, $arguments[0], $arguments[1]); break; case self::ACTION_TYPE_REPORT: return $this->exeReport($meta, $arguments[0], $arguments[1]); break; } } } /** * Contract for session based operation. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conSession($meta, $jobRecord, array $input = array()) { #-> Build contract. if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = (isset($meta['Contract']) && self::CONTRACT_TYPE_RECCURING == $meta['Contract']) ? new \Workspace\Contract\Recurring($options, $requirement) : new \Workspace\Contract\UseOnce($options, $requirement); return $this->buildConditions($contract, $meta); } /** * Store data to session. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array */ public function exeSession($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Store input to session. $session = new \Zend\Session\Container($meta['Namespace']); !isset($session->options) && $session->options = array(); $session->options[] = $contract->options; foreach ($meta['RequiredInput'] as $group => $fields) { isset($session->$group) || $session->$group = array(); $session->$group = array_merge($session->$group, $contract->data->$group); } foreach ($meta['OptionalInput'] as $group => $fields) { isset($session->$group) || $session->$group = array(); !isset($meta['RequiredInput'][$group]) && isset($contract->data->$group) && $session->$group = array_merge($session->$group, $contract->data->$group); } #-> Handle post execution actions. if (isset($meta['ExecuteAfter'])) { foreach ($meta['ExecuteAfter'] as $action) { $this->$action($meta, $jobRecord, $session, $contract); } } #-> Done. return $contract->success('Data stored.'); } /** * Contract to route item to a new state. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conRoute($meta, $jobRecord, array $input = array()) { #-> Build contract. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } if (isset($meta['Surrogate'])) { $baseMeta = $meta['Surrogate']; $meta = $this->$baseMeta; } $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = new \Workspace\Contract\UseOnce($options, $requirement); return $this->buildConditions($contract, $meta); } /** * Route item to a new state. * @param array $meta * @param object|null $jobRecord * @param string $currentState * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeRoute($meta, $jobRecord, $currentState, \Workspace\Utility\ServiceInputParams $contract) { if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($meta['Surrogate'])) { $baseMeta = $meta['Surrogate']; $surrogate = $this->$baseMeta; switch ($surrogate['Type']) { case 'List': $this->exeList($surrogate, $jobRecord, $contract); break; case 'SelectList': $this->exeSelectList($surrogate, $jobRecord, $contract); break; case 'Grid': $this->exeGrid($surrogate, $jobRecord, $contract); break; case 'View': $this->exeView($surrogate, $jobRecord, $contract); break; case 'Create': $this->exeCreate($surrogate, $jobRecord, $contract); break; case 'Update': $this->exeUpdate($surrogate, $jobRecord, $contract); break; case 'Delete': throw new \Exception('Route may not surrogate Delete functionality.'); break; } } return array( 'Destination' => $meta['Destination'], 'Data' => isset($contract->data->Context) ? $contract->data->Context : array() ); } /** * Contract to list entries. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conList($meta, $jobRecord, array $input = array()) { #-> Build contract. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = (isset($meta['Contract']) && self::CONTRACT_TYPE_RECCURING == $meta['Contract']) ? new \Workspace\Contract\Recurring($options, $requirement) : new \Workspace\Contract\UseOnce($options, $requirement); return $this->buildConditions($contract, $meta); } /** * List entries with optional filtering. Will exclude archived entries by default. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeList($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Filtering. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } $baseEntity = $this->meta['Entity']; $criteria = array(); if (isset($this->meta['JobField'])) { $criteria[$this->meta['JobField']] = $jobRecord->id; } isset($contract->data->Filter) && is_array($contract->data->Filter) && !empty($contract->data->Filter) && $criteria = array_merge($criteria, $contract->data->Filter); isset($meta['Filter']) && is_array($meta['Filter']) && !empty($meta['Filter']) && $criteria = array_merge($criteria, $meta['Filter']); $orderBy = !empty($meta['OrderBy']) ? $meta['OrderBy'] : array(); $expand = isset($meta['Expand']) ? $meta['Expand'] : array(); #-> Done. return $contract->success( 'List retrieved.', $this->dataList($meta['Fields'], $expand, $criteria, $orderBy) ); } /** * List entries with optional filtering. Will exclude archived entries by default. * @param array $fields * @param array $expand * @param array $criteria * @return array */ public function dataList(array $fields, array $expand = array(), array $criteria = array(), $orderBy = array()) { #-> Collect entries. $baseEntity = $this->meta['Entity']; $baseEntity::ARCHIVE && !isset($criteria['archived']) && $criteria['archived'] = false; $records = !empty($criteria) ? $this->em->getRepository($this->meta['Entity']) ->findBy($criteria, $orderBy) : $this->em->getRepository($this->meta['Entity']) ->findBy(array(), $orderBy); #-> Cleanup. foreach ($records as $rowId => $record) { $records[$rowId] = $record->toArray($expand, $fields); } #-> Done. return $records; } /** * Contract to list entries in id => label format. Will exclude archived entries by default. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conSelectList($meta, $jobRecord, array $input = array()) { #-> Build contract. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = (isset($meta['Contract']) && self::CONTRACT_TYPE_RECCURING == $meta['Contract']) ? new \Workspace\Contract\Recurring($options, $requirement) : new \Workspace\Contract\UseOnce($options, $requirement); return $this->buildConditions($contract, $meta); } /** * List entries in id => label format with optional filtering. Will exclude archived entries by default. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeSelectList($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Filtering. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } $base = $this->meta['Base']; $baseEntity = $this->meta['Entity']; $criteria = array(); $baseEntity::ARCHIVE && $criteria['archived'] = false; if (isset($this->meta['JobField']) && !is_null($jobRecord)) { $criteria[$this->meta['JobField']] = $jobRecord->id; } isset($contract->data->Filter) && is_array($contract->data->Filter) && !empty($contract->data->Filter) && $criteria = array_merge($criteria, $contract->data->Filter); isset($meta['Filter']) && is_array($meta['Filter']) && !empty($meta['Filter']) && $criteria = array_merge($criteria, $meta['Filter']); #-> Done. return $contract->success( 'List retrieved.', $this->selectList( $meta['Label']['Format'], $meta['Label']['Fields'], $criteria ) ); } /** * List entries in id => label format with optional filtering. Will exclude archived entries by default. * @param string $labelFormat * @param array $labelFields * @param array $criteria * @return array */ public function selectList($labelFormat, array $labelFields, array $criteria = array()) { #-> Collect entries. $baseEntity = $this->meta['Entity']; $baseEntity::ARCHIVE && !isset($criteria['archived']) && $criteria['archived'] = false; $negCriteria = array(); foreach ($criteria as $field => $value) { if (!is_array($value) && !is_object($value) && '!=' == substr($value, 0, 2)) { $negCriteria[$field] = substr($value, 2); unset($criteria[$field]); } } $orderBy = array(); if (isset($labelFields[0])) { $orderBy[$labelFields[0]] = 'ASC'; } $records = !empty($criteria) ? $this->em->getRepository($this->meta['Entity']) ->findBy($criteria, $orderBy) : $this->em->getRepository($this->meta['Entity']) ->findBy(array(), $orderBy); #-> Cleanup. $search = array(); foreach ($labelFields as $key => $fieldName) { !is_numeric($key) && $fieldName = $key; $search[] = '[' . $fieldName . ']'; } $data = array(); #-> Build list. foreach ($records as $rowId => $record) { foreach ($negCriteria as $field => $value) { if ($value == $record->$field) { continue 2; } } $replace = array(); foreach ($labelFields as $key => $fieldName) { !is_numeric($key) && $fieldName = $key; $replace[] = !is_numeric($key) ? (!is_null($record->$key) ? $record->$key : $fieldName) : $record->$fieldName; } $data[] = array( 'value' => $record->id, 'label' => str_replace( $search, $replace, $labelFormat ) ); unset($records[$rowId]); } #-> Done. return $data; } /** * Contract for data grid. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conGrid($meta, $jobRecord, array $input = array()) { #-> Build contract. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } /* if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } */ $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = (isset($meta['Contract']) && self::CONTRACT_TYPE_RECCURING == $meta['Contract']) ? new \Workspace\Contract\Recurring($options, $requirement) : new \Workspace\Contract\UseOnce($options, $requirement); return $this->buildConditions($contract, $meta); } /** * List entries with optional filtering. Will exclude archived entries by default. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeGrid($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Filtering. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } $base = $this->meta['Base']; $baseEntity = $this->meta['Entity']; $criteria = array(); if (isset($this->meta['JobField']) && !is_null($jobRecord)) { $meta['Filter'][$this->meta['JobField']] = $jobRecord->id; } #-> Session memory. $session = new \Zend\Session\Container('DataBin_Grid_' . $contract->hash); if (\Utility\Registry::initSessionStorage('DataBin_Grid_' . $contract->hash)) { $session->Filter = array(); $session->OrderBy = isset($meta['OrderBy']) ? $meta['OrderBy'] : array(); $session->Page = 1; $session->NumberOfRecords = $meta['NumberOfRecords']; } else { !isset($session->Filter) && $session->Filter = array(); !isset($session->NumberOfRecords) && $session->NumberOfRecords = $meta['NumberOfRecords']; !isset($session->Page) && $session->Page = 1; !isset($session->OrderBy) && $session->OrderBy = isset($meta['OrderBy']) ? $meta['OrderBy'] : array(); } isset($contract->data->Grid['NumberOfRecords']) && is_numeric($contract->data->Grid['NumberOfRecords']) && $session->NumberOfRecords = $contract->data->Grid['NumberOfRecords']; isset($contract->data->Grid['Page']) && is_numeric($contract->data->Grid['Page']) && $session->Page = $contract->data->Grid['Page']; isset($contract->data->Grid['Filter']) && is_array($contract->data->Grid['Filter']) && $session->Filter = $contract->data->Grid['Filter']; isset($contract->data->Grid['OrderBy']) && is_array($contract->data->Grid['OrderBy']) && $session->OrderBy = $contract->data->Grid['OrderBy']; #-> Collect entries. $export = ( isset($meta['Export']) && $meta['Export'] && isset($contract->options->ExportToExcel) && $contract->options->ExportToExcel ) ? true : false; if ($export && isset($meta['ExportMeta'])) { $swap = $meta['ExportMeta']; foreach ($swap as $key => $item) { $meta[$key] = $item; } } $response = $this->grid( $meta['Query'], $meta['Selection'], $session->NumberOfRecords, $session->Page, array_merge( $meta['Filter'], $session->Filter ), $session->OrderBy, isset($meta['GroupBy']) ? $meta['GroupBy'] : '', $meta['Fields'], $meta['Base'], $export, isset($meta['Swap']) ? $meta['Swap'] : '' ); $response['Meta']['Filters'] = $session->Filter; #-> Check if an excel export is allowed and requested. if ($export) { #-> Build excel report. $reportService = $meta['Builder']; $report = new $reportService(); $report->process(array(), array(), $response['DataSet']); $reportWriter = $meta['Writer']; #-> Output. $output = isset($meta['Output']) ? $meta['Output'] : 'Download'; if ('Download' == $output) { $writer = new $reportWriter($report); $writer->output(); exit(0); } elseif ('File' == $output) { $writer = new $reportWriter($report); $writer->output($meta['FilePath']); return $contract->success('Report saved to file.'); } elseif ('Raw' == $output) { return $contract->success('Report Generated', array( 'Title' => $report->getTitle(), 'Description' => $report->getDescription(), 'QueryDetails' => $report->getQueries(), 'Headers' => $report->getHeaders(), 'Fields' => $report->getFields(), 'TotalFields' => $report->getTotalFields(), 'Notes' => $report->getNotes(), 'Data' => $report->getDataIntersection() )); } else { $writer = new $reportWriter($report); return $contract->success('Report Generated', $writer->output()); } } #-> Done. return $contract->success('Grid retrieved.', $response); } /** * Retrieve data grid from provided query. * The DQL query should contain [WHERE] and [ORDER] for relevant hydration. * Filtering allows for smart filtering: 'profile.firstName' => '!NULL'. * numberOfRecords AND page must be greater than 0 to collect a paged dataset. * * @param string $dql * @param string $selection * @param integer $numberOfRecords * @param integer $page * @param array $filter * @param array $order * @param array $fields * @return array */ public function grid($dql, $selection, $numberOfRecords, $page, array $filter = array(), array $order = array(), $group = '', array $fields = array(), $baseTable = '', $forExport = false, $swap = '') { #-> Establish size of dataset. !empty($group) && $group = 'GROUP BY ' . $group; $where = \Utility\Doctrine::dqlFilter($filter, $baseTable); $query = str_replace( array('[SELECTION]', '[WHERE]', '[ORDER]', '[GROUP]', '[SWAP]'), array("COUNT(DISTINCT $baseTable.id) AS total", $where['Where'], '', ''), $dql ); /* if ('development' == \Utility\Registry::getConfigParam('Instance')) //1517 == \Utility\Registry::getAuthParam('id')) { \Utility\Debug::errorLog('----------------------', '----------------------'); \Utility\Debug::errorLog('----------------------', '----------------------'); \Utility\Debug::errorLog('----------------------', '----------------------'); \Utility\Debug::errorLog('GRID COUNT QUERY', $query); \Utility\Debug::errorLog('GRID COUNT PARAMS', $where['Params']); } */ $query = $this->em->createQuery($query); !empty($where['Params']) && $query->setParameters($where['Params']); //\Utility\Debug::errorLog('COUNT', $query->getSQL()); $numRecsRes = $query->getSingleResult(); $numRecs = (int) $numRecsRes['total']; if (0 == $numberOfRecords) { $numPages = (0 < $numRecs) ? 1 : 0; } else { $numPages = (0 < $numRecs) ? ceil($numRecs / $numberOfRecords) : 0; } #-> Retrieve paged dataset. $query = str_replace( array('[SELECTION]', '[WHERE]', '[ORDER]', '[GROUP]', '[SWAP]'), array($selection, $where['Where'], \Utility\Doctrine::dqlOrder($order), $group, $swap), $dql ); /* if ('development' == \Utility\Registry::getConfigParam('Instance')) { \Utility\Debug::errorLog('----------------------', '----------------------'); \Utility\Debug::errorLog('GRID QUERY', $query); \Utility\Debug::errorLog('GRID PARAMS', $where['Params']); } */ $query = $this->em->createQuery($query); !empty($where['Params']) && $query->setParameters($where['Params']); if (!$forExport) { (0 < $numberOfRecords) && $query->setMaxResults($numberOfRecords); (0 < $page && 0 < $numberOfRecords) && $query->setFirstResult(($page - 1) * $numberOfRecords); } // \Utility\Debug::errorLog('QUERY', $query->getSQL()); // \Utility\Debug::errorLog('PARAMS', $query->getParameters()); /* if ('development' == \Utility\Registry::getConfigParam('Instance')) { \Utility\Debug::errorLog('----------------------', '----------------------'); \Utility\Debug::errorLog('GRID DATA', $query->getArrayResult()); } */ return array( 'Meta' => array( 'TotalRecords' => $numRecs, 'TotalPages' => $numPages, 'CurrentPage' => $page, 'Filters' => $filter, 'Order' => $order ), 'DataSet' => (!$forExport ? \Utility\Doctrine::extractData($fields, $query->getArrayResult()) : $query->getScalarResult()) ); } /** * Contract to create new entry. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conCreate($meta, $jobRecord, array $input = array()) { #-> Build contract. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = (isset($meta['Contract']) && self::CONTRACT_TYPE_RECCURING == $meta['Contract']) ? new \Workspace\Contract\Recurring($options, $requirement) : new \Workspace\Contract\UseOnce($options, $requirement); return $this->buildConditions($contract, $meta); } /** * Create new entry. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeCreate($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Context data publication. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } isset($contract->data->Context) && \Utility\Registry::setOnce( 'Service.' . $this->meta['Base'] . '.Context', $contract->data->Context ); #-> Create entry. $base = $this->meta['Base']; $data = $contract->data; if (isset($meta['ExecuteBefore'])) { foreach ($meta['ExecuteBefore'] as $action) { $data = $this->$action($meta, $data); } } $data = $data->$base; if (isset($meta['RelatedEntityFromInput'])) { foreach ($meta['RelatedEntityFromInput'] as $context => $relation) { $workflow = $relation['Workflow']; $service = $relation['Service']; $service = new $service(); $service->setWorkflow(new $workflow()); $relId = $service->create($contract->data->$context)->id; $data[$relation['Field']] = $relId; } } if (isset($this->meta['JobField'])) { $data[$this->meta['JobField']] = $jobRecord->id; } $record = $this->create($data); #-> Handle post execution actions. $feed = null; if (isset($meta['ExecuteAfter'])) { foreach ($meta['ExecuteAfter'] as $action) { $feed = $this->$action($meta, $jobRecord, $record, $contract); } } #-> Done. return $contract->success('Entry created.', array( 'id' => $record->id, 'feed' => $feed )); } /** * Create a new entry. * @param array $data * @return object */ public function create(array $data) { #-> Create entry. $base = $this->meta['Base']; $baseEntity = $this->meta['Entity']; if (isset($this->meta['References']) && !empty($this->meta['References'])) { foreach ($this->meta['References'] as $field => $entity) { isset($data[$field]) && $data[$field] = $this->em->getReference($entity, $data[$field]); } } $record = new $baseEntity(); $record->fromArray($data); $this->em->persist($record); $this->em->flush(); #-> Check for post-create requirement. if (defined($baseEntity . '::HAVE_POST_INSERT')) { $record->postInsert(); $this->em->flush(); } #-> Broadcast the data change. /*switch ($baseEntity::PUSH_SYNCH_STRATEGY) { case 'Build': \Utility\Comms\Ape::broadcastBuildDatasetChange($this->meta['DatasetName']); break; case 'Update': if (defined($baseEntity . '::JOB_QUEUE')) { if (1 != $record->queueStatus) { break; } } \Utility\Comms\Ape::broadcastUpdateDatasetChange( $this->meta['DatasetName'], \Utility\Comms\Ape::CHANGE_TYPE_CREATE, array($record->toSynchArray()) ); break; }*/ \Utility\Event::trigger($this->meta['Base'] . '.Create', $record); return $record; } /** * Contract to view an entry. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conView($meta, $jobRecord, array $input = array()) { #-> Safety checks. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } if (!isset($input['id']) || !is_numeric($input['id']) || 0 == $input['id']) { throw new \Exception('A valid record `id` is required to setup the contract.'); } $base = $this->meta['Base']; $record = $this->em ->getRepository($this->meta['Entity']) ->find($input['id']); if (is_null($record)) { throw new \Exception('Could not find record.'); } if (isset($this->meta['JobField'])) { $field = $this->meta['JobField']; if ($record->$field->id != $jobRecord->id) { throw new \Exception('This record does not belong to the specified JobId.'); } } #-> Build contract. $contract = new \Workspace\Contract\Recurring( new \Workspace\UseCase\Options(), new \Workspace\UseCase\Requirement() ); return $contract->setData($record->toArray( is_array($meta) && isset($meta['Expand']) ? $meta['Expand'] : array(), is_array($meta) && isset($meta['Intersect']) ? $meta['Intersect'] : array(), false, isset($meta['ExpandDepth']) ? $meta['ExpandDepth'] : 1 )); } /** * View an entry. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeView(array $meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Retrieve entry. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } return $contract->success('Entry retrieved.', $this->view( $contract->data->id, isset($meta['Expand']) ? $meta['Expand'] : array(), isset($meta['Intersect']) ? $meta['Intersect'] : array(), false, isset($meta['ExpandDepth']) ? $meta['ExpandDepth'] : 1 )); } /** * View an entry. * @param integer $id * @param array $expand * @param array $intersect * @param boolean $showIdentifiers * @param integer $expandAll * @return array */ public function view( $id, array $expand = array(), array $intersect = array(), $showIdentifiers = false, $expandAll = 1 ) { #-> Retrieve entry. $record = $this->em->getReference($this->meta['Entity'], $id); return $record->toArray($expand, $intersect, $showIdentifiers, $expandAll); } /** * Contract to update an entry. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conUpdate($meta, $jobRecord, array $input = array()) { #-> Safety checks. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } if (!isset($input['id']) || !is_numeric($input['id']) || 0 == $input['id']) { throw new \Exception('A valid record `id` parameter in the `Packet` root is required to setup the contract.'); } $base = $this->meta['Base']; $record = $this->em ->getRepository($this->meta['Entity']) ->find($input['id']); if (is_null($record)) { throw new \Exception('Could not find record.'); } if (isset($this->meta['JobField'])) { $field = $this->meta['JobField']; if ($record->$field->id != $jobRecord->id) { throw new \Exception('This record does not belong to the specified JobId.'); } } #-> Build contract. $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = (isset($meta['Contract']) && self::CONTRACT_TYPE_RECCURING == $meta['Contract']) ? new \Workspace\Contract\Recurring($options, $requirement) : new \Workspace\Contract\UseOnce($options, $requirement); $contract = $this->buildConditions($contract, $meta); #-> Handle additional contract. if (isset($meta['ConditionalContract'])) { foreach ($meta['ConditionalContract'] as $action) { $this->$action($meta, $jobRecord, $record, $contract); } } #-> Done. $expand = isset($meta['Expand']) ? $meta['Expand'] : array(); return $contract->setData($record->toArray($expand, array(), true)); } /** * Update an entry. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeUpdate($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Context data publication. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } isset($contract->data->Context) && \Utility\Registry::setOnce( 'Service.' . $this->meta['Base'] . '.Context', $contract->data->Context ); #-> Handle related entity updates. $base = $this->meta['Base']; $record = null; $data = isset($contract->data->$base) ? $contract->data->$base : array(); if (isset($meta['ExecuteBefore'])) { foreach ($meta['ExecuteBefore'] as $action) { $res = $this->$action($meta, $jobRecord, $contract, $data); if (is_object($res)) { $contract->data = $res; $data = isset($contract->data->$base) ? $contract->data->$base : array(); } else { $data = $res; } } } if (isset($meta['RelatedEntityFromInput'])) { $record = $this->em->getRepository($this->meta['Entity']) ->find($contract->data->id); foreach ($meta['RelatedEntityFromInput'] as $context => $relation) { $workflow = $relation['Workflow']; $service = $relation['Service']; $field = $relation['Field']; $service = new $service(); $service->setWorkflow(new $workflow()); if (!is_null($record->$field)) { $service->update( $record->$field->id, $contract->data->$context ); } else { $relId = $service->create($contract->data->$context)->id; $data[$field] = $relId; } } } #-> Update entry. !empty($data) && $record = $this->update( $contract->data->id, $data, $record ); #-> Handle post execution actions. $feed = null; if (isset($meta['ExecuteAfter'])) { is_null($record) && $record = $this->em->getRepository($this->meta['Entity']) ->find($contract->data->id); foreach ($meta['ExecuteAfter'] as $action) { $feed = $this->$action($meta, $jobRecord, $record, $contract); } } #-> Done. return $contract->success('Entry updated.', array( 'id' => !is_null($record) ? $record->id : $contract->data->id, 'feed' => $feed )); } /** * Update an entry. * @param integer $id * @param array $data * @param object|null $record * @return object */ public function update($id, array $data, $record = null) { #-> Update entry. $base = $this->meta['Base']; $baseEntity = $this->meta['Entity']; $record = is_null($record) ? $this->em->getRepository($this->meta['Entity']) ->find($id) : $record; defined($baseEntity . '::JOB_QUEUE') && $queueStatus = $record->queueStatus; if (isset($this->meta['References']) && !empty($this->meta['References'])) { foreach ($this->meta['References'] as $field => $entity) { $data[$field] = isset($data[$field]) && !empty($data[$field]) ? $this->em->getReference($entity, $data[$field]) : null; } } $record->fromArray($data); $this->em->flush(); #-> Check for post-update requirement. if (defined($baseEntity . '::HAVE_POST_UPDATE')) { $record->postUpdate(); $this->em->flush(); } #-> Broadcast the data change. /*switch ($baseEntity::PUSH_SYNCH_STRATEGY) { case 'Build': \Utility\Comms\Ape::broadcastBuildDatasetChange($this->meta['DatasetName']); break; case 'Update': $type = \Utility\Comms\Ape::CHANGE_TYPE_UPDATE; $updateData = $record->toSynchArray(); if (defined($baseEntity . '::JOB_QUEUE')) { if (1 != $record->queueStatus && 1 != $queueStatus) { break; } else { if ($record->queueStatus == $queueStatus) { $type = \Utility\Comms\Ape::CHANGE_TYPE_UPDATE; } elseif (0 == $queueStatus) { $type = \Utility\Comms\Ape::CHANGE_TYPE_CREATE; } else { $type = \Utility\Comms\Ape::CHANGE_TYPE_DELETE; $updateData = array('id' => $record->id); } } } \Utility\Comms\Ape::broadcastUpdateDatasetChange( $this->meta['DatasetName'], $type, array($updateData) ); break; }*/ \Utility\Event::trigger($this->meta['Base'] . '.Update', $record); return $record; } /** * Contract to delete an entry. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conDelete($meta, $jobRecord, array $input = array()) { #-> Safety checks. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } if (!isset($input['id']) || !is_numeric($input['id']) || 0 == $input['id']) { throw new \Exception('A valid record `id` is required to setup the contract.'); } $base = $this->meta['Base']; $record = $this->em ->getRepository($this->meta['Entity']) ->find($input['id']); if (is_null($record)) { throw new \Exception('Could not find record.'); } if (isset($this->meta['JobField'])) { $field = $this->meta['JobField']; if ($record->$field->id != $jobRecord->id) { throw new \Exception('This record does not belong to the specified JobId.'); } } #-> Build contract. $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = (isset($meta['Contract']) && self::CONTRACT_TYPE_RECCURING == $meta['Contract']) ? new \Workspace\Contract\Recurring($options, $requirement) : new \Workspace\Contract\UseOnce($options, $requirement); $contract = $this->buildConditions($contract, $meta); return $contract->setData($record->toArray(array(), array(), true)); } /** * Delete an entry. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeDelete($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Context data publication. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } isset($contract->data->Context) && \Utility\Registry::setOnce( 'Service.' . $this->meta['Base'] . '.Context', $contract->data->Context ); #-> Execute before. $record = $this->em->getRepository($this->meta['Entity']) ->find($contract->data->id); $base = $this->meta['Base']; $data = isset($contract->data->$base) ? $contract->data->$base : array(); if (isset($meta['ExecuteBefore'])) { foreach ($meta['ExecuteBefore'] as $action) { $data = $this->$action($meta, $record, $contract, $data); } } #-> Handle related entity updates. if (isset($meta['RelatedEntityFromInput'])) { foreach ($meta['RelatedEntityFromInput'] as $context => $relation) { $workflow = $relation['Workflow']; $service = $relation['Service']; $field = $relation['Field']; $service = new $service(); $service->setWorkflow(new $workflow()); $service->delete($record->$field->id); } } #-> Delete/Archive entry. $this->delete($contract->data->id, $record); #-> Handle post execution actions. if (isset($meta['ExecuteAfter'])) { foreach ($meta['ExecuteAfter'] as $action) { $this->$action($meta, $jobRecord, $record, $contract); } } #-> Done. return $contract->success('Entry deleted.', array( 'id' => $contract->data->id )); } /** * Delete an entry. * @param integer $id * @param object|null $record * @return boolean */ public function delete($id, $record = null) { #-> Delete/Archive entry. $base = $this->meta['Base']; $baseEntity = $this->meta['Entity']; $record = is_null($record) ? $this->em->getReference($this->meta['Entity'], $id) : $record; if ($baseEntity::ARCHIVE) { $record->archived = true; } else { $this->em->remove($record); } $this->em->flush(); #-> Broadcast the data change. /*switch ($baseEntity::PUSH_SYNCH_STRATEGY) { case 'Build': \Utility\Comms\Ape::broadcastBuildDatasetChange($this->meta['DatasetName']); break; case 'Update': if (defined($baseEntity . '::JOB_QUEUE')) { $record = $this->em->getRepository($this->meta['Entity']) ->find($id); if (1 != $record->queueStatus) { break; } } \Utility\Comms\Ape::broadcastUpdateDatasetChange( $this->meta['DatasetName'], \Utility\Comms\Ape::CHANGE_TYPE_DELETE, array(array('id' => $id)) ); break; }*/ \Utility\Event::trigger($this->meta['Base'] . '.Delete', $record); return true; } /** * Contract to undelete an entry. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conUnDelete($meta, $jobRecord, array $input = array()) { #-> Check permissions. $this->checkPermissions($meta); #-> Safety checks. if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } if (!isset($input['id']) || !is_numeric($input['id']) || 0 == $input['id']) { throw new \Exception('A valid record `id` is required to setup the contract.'); } $entityName = '\\' == substr($this->meta['Entity'], 0, 1) ? substr($this->meta['Entity'], 1) : $this->meta['Entity']; $record = $this->em ->getRepository($entityName) ->find($input['id']); if (is_null($record)) { throw new \Exception('Could not find record.'); } if (isset($this->meta['JobField'])) { $field = $this->meta['JobField']; if ($record->$field->id != $jobRecord->id) { throw new \Exception('This record does not belong to the specified JobId.'); } } #-> Build contract. $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = (isset($meta['Contract']) && self::CONTRACT_TYPE_RECCURING == $meta['Contract']) ? new \Workspace\Contract\Recurring($options, $requirement) : new \Workspace\Contract\UseOnce($options, $requirement); $contract = $this->buildConditions($contract, $meta); return $contract->setData($record->toArray(array(), array(), true)); } /** * UnDelete an entry. * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array */ public function exeUnDelete($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Context data publication. isset($contract->data->Context) && \Utility\Registry::setOnce( 'Service.' . $this->meta['Base'] . '.Context', $contract->data->Context ); #-> Handle related entity updates. $entityName = '\\' == substr($this->meta['Entity'], 0, 1) ? substr($this->meta['Entity'], 1) : $this->meta['Entity']; $record = $this->em->getRepository($entityName)->find($contract->data->id); if (isset($meta['RelatedEntityFromInput'])) { foreach ($meta['RelatedEntityFromInput'] as $context => $relation) { $workflow = $relation['Workflow']; $service = $relation['Service']; $field = $relation['Field']; $service = new $service(); $service->setWorkflow(\Utility\Registry::getServiceManager()->get($workflow)); $service->undelete($record->$field->id); } } #-> Delete/Archive entry. $this->undelete($contract->data->id, $record); #-> Handle post execution actions. if (isset($meta['ExecuteAfter'])) { foreach ($meta['ExecuteAfter'] as $action) { $this->$action($meta, $jobRecord, $record, $contract); } } #-> Done. return $contract->success('Entry deleted.', array( 'id' => $contract->data->id )); } /** * UnDelete an entry. * @param integer $id * @param object|null $record * @return boolean */ public function undelete($id, $record = null) { #-> Delete/Archive entry. $base = $this->meta['Base']; $baseEntity = $this->meta['Entity']; $this->em->clear(); $record = $this->em->getRepository($this->meta['Entity'])->find($id); $record->archived = false; $this->em->flush(); #-> Clear cache. $base = $this->meta['Base']; #-> Broadcast the data change. /*switch ($baseEntity::PUSH_SYNCH_STRATEGY) { case 'Build': \Utility\Comms\Ape::broadcastBuildDatasetChange($this->meta['DatasetName']); break; case 'Update': if (defined($baseEntity . '::JOB_QUEUE')) { $record = $this->em->getRepository($this->meta['Entity']) ->find($id); if (1 != $record->queueStatus) { break; } } \Utility\Comms\Ape::broadcastUpdateDatasetChange( $this->meta['DatasetName'], \Utility\Comms\Ape::CHANGE_TYPE_UNDELETE, array(array('id' => $id)) ); break; }*/ $action = $baseEntity::ARCHIVE ? '.UnArchive' : '.UnDelete'; \Utility\Event::trigger($this->meta['Base'] . $action, $record); return true; } /** * Contract to build a report. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conReport($meta, $jobRecord, array $input = array()) { #-> Build contract. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = new \Workspace\Contract\Recurring($options, $requirement); return $this->buildConditions($contract, $meta); } /** * Build a report * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exeReport($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Build Report. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } $options = $contract->options; $input = isset($contract->data->Report) ? $contract->data->Report : array(); if (isset($this->meta['JobField'])) { $input[$this->meta['JobField']] = $jobRecord->id; } $reportService = $meta['Builder']; $report = new $reportService(); $report->process($input, $options); $reportWriter = $meta['Writer']; #-> Output. $output = isset($meta['Output']) ? $meta['Output'] : 'Download'; if ('Download' == $output) { $writer = new $reportWriter($report); $writer->output(); exit(0); } elseif ('File' == $output) { $writer = new $reportWriter($report); $writer->output($meta['FilePath']); return $contract->success('Report saved to file.'); } elseif ('Raw' == $output) { return $contract->success('Report Generated', array( 'Title' => $report->getTitle(), 'Description' => $report->getDescription(), 'QueryDetails' => $report->getQueries(), 'Headers' => $report->getHeaders(), 'Fields' => $report->getFields(), 'TotalFields' => $report->getTotalFields(), 'Notes' => $report->getNotes(), 'Data' => $report->getDataIntersection() )); } else { $writer = new $reportWriter($report); return $contract->success('Report Generated', $writer->output()); } } /** * Contract to build a report. * @param array $meta * @param object|null $jobRecord * @param array $input * @return \Workspace\Contract\AbstractBase * @throws \Exception */ public function conPdf($meta, $jobRecord, array $input = array()) { #-> Build contract. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } if (isset($this->meta['JobField']) && is_null($jobRecord)) { throw new \Exception('`JobId` root parameter is required for this contract.'); } $options = $this->buildOptions($meta); $requirement = $this->buildRequirements($meta); $contract = new \Workspace\Contract\Recurring($options, $requirement); return $this->buildConditions($contract, $meta); } /** * Build a report * @param array $meta * @param object|null $jobRecord * @param \Workspace\Utility\ServiceInputParams $contract * @return array * @throws \Exception */ public function exePdf($meta, $jobRecord, \Workspace\Utility\ServiceInputParams $contract) { #-> Build Report. if (isset($meta['RequireAuth']) && true == $meta['RequireAuth'] && !\Utility\Registry::isAuthenticated()) { throw new \Exception('Authentication required for this functionality.'); } $options = $contract->options; $input = isset($contract->data->Report) ? $contract->data->Report : array(); $input['jobRecord'] = $jobRecord; $input['contract'] = $contract->data; if (isset($this->meta['JobField'])) { $input[$this->meta['JobField']] = $jobRecord->id; } $pdfService = $meta['Builder']; $pdf = new $pdfService(); $pdf->process($input, $options); $pdfWriter = $meta['Writer']; #-> Output. $output = isset($meta['Output']) ? $meta['Output'] : 'Download'; if ('Download' == $output) { $writer = new $pdfWriter($pdf); $writer->output(); exit(0); } elseif ('File' == $output) { $writer = new $pdfWriter($pdf); file_put_contents($meta['FilePath'], $writer->output('')); return $contract->success('PDF saved to file.'); } elseif ('Raw' == $output) { return $contract->success('PDF Generated', array( 'Title' => $pdf->getTitle(), 'HTML' => $pdf->getHtml() )); } else { $writer = new $pdfWriter($pdf); return $contract->success('PDF Generated', $writer->output()); } } }