Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <?php
- namespace T8LB\Spec\Designer\Reports;
- use Illuminate\Validation\ValidationException;
- use T8LB\Spec\Designer\Services\Reports\BlockReader;
- use T8LB\Spec\Designer\Services\Reports\DateCompute;
- /**
- * Class Block
- * @package T8LB\Spec\Designer\Reports
- */
- class Block
- {
- // TODO: Before giving filteredData, make sure only the finalColumns are sent back -- DONE
- // TODO: Validate XML Files with filename and top level name attribute -- [Blocks don't have top level names]
- // TODO: Fix bug of setFinalColumnNames failing when there are no titles -- DONE
- // TODO: Disallow same names for columns -- DONE
- // TODO: Compare block and brick columns. Block cannot have more columns than brick -- VERIFY
- // TODO: Validate - If any column in the Block does not exist in the brick, its a fail -- DONE
- // TODO: Validate - Check if Brick in Block's relations exist -- DONE
- private $xml = [];
- private $brickFilenames = [];
- private $columns,
- $blockName,
- $visibleColumns,
- $nonVisibleColumns,
- $nameToTitleList,
- $finalColumnNames,
- $allBrickColumns,
- $virtualColumnsWithInfo,
- $computedVirtualColumns = [],
- $finalOutput,
- $leftColumn,
- $rightColumn,
- $rightBrick,
- $leftBrick,
- $join,
- $path,
- $extension;
- /**
- * Block constructor.
- * @param $blockName
- * @throws ValidationException
- */
- public function __construct($blockName)
- {
- $this->blockName = $blockName;
- try {
- $this->loadConfiguration($this->blockName);
- } catch (ValidationException $e) {
- throw $e;
- }
- try {
- $this->validateConfiguration();
- } catch (ValidationException $e) {
- throw $e;
- }
- // $dateCompute = new DateCompute();
- //
- // pr($dateCompute->compute("currentmonth(1)"), 1);
- }
- /**
- * @param $blockName
- * @throws ValidationException
- */
- private function loadConfiguration($blockName)
- {
- # Define Path for Accessing XML
- $this->path = storage_path('app/reports/');
- # Define Extension for the File
- $this->extension = '.xml';
- # Read the Given File if XML
- $data = new BlockReader($this->path . $blockName . $this->extension);
- try {
- $this->xml = $data->getStructuredData();
- } catch (ValidationException $e) {
- throw $e;
- }
- # Get Bricks Info
- $bricks = $this->xml['Block']['Bricks'];
- # Relations
- foreach ($this->xml['Block']['Relations'] as $key => $relation) {
- $this->leftColumn[$key] = $relation['RelationColumn']['attributes']['BrickLeft'];
- $this->rightColumn[$key] = $relation['RelationColumn']['attributes']['BrickRight'];
- $this->rightBrick[$key] = $relation['attributes']['BrickRight'];
- $this->leftBrick[$key] = $relation['attributes']['BrickLeft'];
- $this->join[$key] = $relation['attributes']['Type'];
- }
- # Get Bricks File Names from Block XML
- foreach ($bricks as $brick)
- $this->brickFilenames[] = $brick['attributes']['Name'];
- $this->populateColumns($this->xml['Block']['Columns']);
- # Set properties like visibility and titles (instead of names)
- $this->setColumnProperties();
- # Final columns that will be used for output
- $this->setFinalColumnNames();
- }
- /**
- * @throws ValidationException
- */
- private function validateConfiguration()
- {
- $errorList = [];
- # Check if names of any column in the block is empty
- foreach ($this->columns as $column) {
- if ($column->getName() == "") {
- $errorList[] = "Name attribute for column does not exist in Block - " . $this->blockName;
- }
- }
- # Check if the columns given in block exists in the associated bricks
- try {
- $checkColumnsExistInBrick = $this->checkColumnsExistInBrick();
- } catch (ValidationException $e) {
- throw $e;
- }
- if ($checkColumnsExistInBrick !== '') {
- $errorList[] = $checkColumnsExistInBrick;
- }
- if (count($errorList) > 0) {
- throw ValidationException::withMessages($errorList);
- }
- }
- /**
- * @return string
- * @throws ValidationException
- */
- private function checkColumnsExistInBrick()
- {
- $brickColumnArrays = [];
- foreach ($this->brickFilenames as $brickFilename) {
- try {
- $brick = new Brick($brickFilename);
- } catch (ValidationException $e) {
- throw $e;
- }
- $brickColumnArrays[] = $brick->getColumns();
- }
- # Merge columns from different bricks into one
- $this->allBrickColumns = [];
- foreach ($brickColumnArrays as $columnArray) {
- foreach ($columnArray as $column) {
- $this->allBrickColumns[] = $column;
- }
- }
- # Check if the block column exists in the brick
- foreach ($this->columns as $blockColumn) {
- // Don't check if it is a virtual column
- if ($blockColumn->getIsVirtual() == true) {
- continue;
- }
- $found = false;
- foreach ($this->allBrickColumns as $brickColumn) {
- if ($blockColumn->getName() === $brickColumn->getName()) {
- # Found column
- $found = true;
- break;
- }
- }
- if (!$found) {
- return "The column '" . $blockColumn->getName() . "' was not found in bricks";
- }
- }
- return '';
- }
- public function getData($requiredColumns, $displayFormat, $allColumns = false)
- {
- if (count($requiredColumns) == 0) {
- throw ValidationException::withMessages(["Minimum one column is required."]);
- }
- $bricks = [];
- $result = [];
- # Get Columns and Source Data from XML
- foreach ($this->brickFilenames as $brickFilename) {
- try {
- $brick = new Brick($brickFilename);
- } catch (ValidationException $e) {
- throw $e;
- }
- $brickInfo = $brick->getSQL();
- $bricks[$brickFilename] = [
- 'sqlData' => $brickInfo['sql'],
- 'columns' => $brickInfo['columns']
- ];
- }
- # Execute the SQL
- foreach ($bricks as $key => $value)
- $result[$key] = $value['sqlData'];
- # Get individual column names
- foreach ($bricks as $key => $value)
- $columns[$key] = $value['columns'];
- # Merge All Relations One by One
- $output = $result[$this->leftBrick[0]];
- foreach ($this->xml['Block']['Relations'] as $key => $relation) {
- $alias = range('A', 'Z');
- # Right Brick Data Collection
- $rightBrick = ($result[$this->rightBrick[$key]]);
- # Get Left & Right Keys
- $left = $this->leftColumn[$key];
- $right = $this->rightColumn[$key];
- # Get Join Type
- $joinType = $this->join[$key];
- # Get Left Brick Keys
- foreach ($columns[$this->leftBrick[$key]] as $column1)
- $setLeftColumns[$key][] = $column1->Name;
- # Alias Left Brick Keys
- foreach ($setLeftColumns[$key] as $column1Key => $column1)
- $setLeftColumns[$key][$column1Key] = $column1 . ' AS ' . $alias[$key] . $alias[$key] . '_' . $column1;
- # Get Right Brick Keys
- foreach ($columns[$this->rightBrick[$key]] as $column2)
- $setRightColumns[$key][] = $column2->Name;
- # Alias Right Brick Keys
- foreach ($setRightColumns[$key] as $column1Key => $column1) {
- $setRightColumns[$key][$column1Key] = $column1 . ' AS ' . $alias[$key + 1] . $alias[$key + 1] . '_' . $column1;
- $aliasedRightKeys[$key][$column1] = $alias[$key + 1] . $alias[$key + 1] . '_' . $column1;
- }
- # Get Aliased Right Key
- $newRightKey = $alias[$key + 1] . $alias[$key + 1] . '_' . $right;
- # Get Aliased Right Columns
- $rightColumns = implode(',', $setRightColumns[$key]);
- # Check if Right DataSet is Empty & Generate Join SQL Statement
- if (!empty($rightBrick))
- $output = $this->join($output, $rightBrick, $left, $joinType, $rightColumns, $alias[$key], $alias[$key + 1], $newRightKey);
- }
- # Get Final Columns
- foreach ($this->columns as $column) {
- if ($column->getIsVirtual() != true) {
- $finalColumnSet[] = $column->Name;
- }
- }
- # Replace Original Keys with Aliased Keys
- foreach ($aliasedRightKeys as $formatted) {
- foreach ($formatted as $key => $format) {
- $exist = array_search($key, $finalColumnSet);
- if ($exist >= 0) {
- $keyToBeReplaced = array_search($key, $finalColumnSet);
- $finalColumnSet[$keyToBeReplaced] = $format;
- }
- }
- }
- # Get 'Select Column' List for the Final Query
- $finalColumns = implode(',', $finalColumnSet);
- $sql = "SELECT " . $finalColumns . " FROM (" . $output . ") AS FinalQuery";
- # Load the SQL to the Brick and get SQL executed
- try {
- $finalBrick = new Brick(null, $sql);
- } catch (ValidationException $e) {
- }
- $dataFromBrick = $finalBrick->getData();
- # Get Executed SQL result
- $sqlResult = $dataFromBrick['sql'];
- # Replace Alias Keys with Original Keys
- foreach ($sqlResult as $result) {
- $finalColumnSet = array_keys((array)$result);
- foreach ($aliasedRightKeys as $aliasedKey) {
- foreach ($aliasedKey as $originalKey => $alias) {
- $exist = array_search($alias, $finalColumnSet);
- if (!empty($exist) && $exist >= 0) {
- if (isset($result->$alias)) $result->{$originalKey} = $result->$alias;
- unset($result->$alias);
- }
- }
- }
- }
- $dataAfterVirtualColumns = $this->computeVirtualColumns($sqlResult);
- # Final Output
- $resultSet = $this->filterData($dataAfterVirtualColumns, $requiredColumns, $allColumns);
- # Format Result Set Based on the Request table format by default and JSON is requested.
- if ($displayFormat !== 'json') {
- $requiredColumns = (count($requiredColumns) > 0) ? $requiredColumns : $this->finalColumnNames;
- $this->finalOutput = $this->toTable($requiredColumns, $resultSet);
- } else
- $this->finalOutput = $resultSet;
- # Return Final result
- return $this->finalOutput;
- }
- private $tmpComputationOperators = [];
- private function computeVirtualColumns($data) {
- // Start with $data then start modifying (adding new keys)
- $modifiedData = $data;
- // Compute object
- $compute = new DateCompute();
- $x = 0;
- // Loop through each object in the dataset
- foreach ($modifiedData as $datum) {
- // Loop through the virtual columns
- foreach ($this->virtualColumnsWithInfo as $columnName => $formula) {
- if (!is_string($formula)) {
- throw ValidationException::withMessages(['The given formula is invalid']);
- }
- // rtrim() because of the space created by the getDerivedFormula... function
- $formulaWithoutOperators = rtrim($this->getDerivedFormulaWithoutOperators($formula, $datum));
- $formulaWithoutOperatorsTerms = explode(" ", $formulaWithoutOperators);
- // Get the without column names formula as a main term (does not consider column names in parameters)
- $i = 0;
- $computableFormula = "";
- foreach ($formulaWithoutOperatorsTerms as $term) {
- if ($i == count($this->tmpComputationOperators)) {
- // If its the last term, just concatenate the term and break out of the loop
- $computableFormula .= $term;
- break;
- }
- $computableFormula .= $term . " " . $this->tmpComputationOperators[$i] . " ";
- $i++;
- }
- foreach ($this->finalColumnNames as $finalColumnName) {
- if (strpos($computableFormula, $finalColumnName) !== false) {
- if (!array_key_exists($finalColumnName, $this->virtualColumnsWithInfo) && !empty($datum->$finalColumnName)) {
- // Not a virtual column, so just get the value from data and replace
- $formulaWithoutOperatorsTerms[] = $datum->$finalColumnName;
- } else if (array_key_exists($finalColumnName, $this->virtualColumnsWithInfo)) {
- // Column is a virtual column, calculate that value for that
- } else {
- // No value exists for the column
- }
- }
- }
- $this->tmpComputationOperators = [];
- $performComputation = $compute->compute($computableFormula);
- $modifiedData[$x]->$columnName = $performComputation;
- }
- $x++;
- }
- return $modifiedData;
- }
- private function getDerivedFormulaWithoutOperators($formula, $datumToModify) {
- // Get every term into an array
- $terms = explode(' ', $formula);
- $termsWithoutOperators = array_filter($terms, function($value){
- $arr = ['+', '-', '*', '/', '%'];
- if (!in_array($value, $arr)) {
- return $value;
- } else {
- $this->tmpComputationOperators[] = $value;
- }
- });
- $formulaWithoutOperators = "";
- // Loop through each term and check if it is a column
- foreach ($termsWithoutOperators as $term) {
- if (in_array($term, $this->finalColumnNames)) {
- // The term is a column
- if (!array_key_exists($term, $this->virtualColumnsWithInfo)) {
- // Term is not a virtual column
- $termValue = $datumToModify->$term;
- $formulaWithoutOperators .= $termValue . " ";
- } else {
- // Term is a virtual column
- if (!array_key_exists($term, $this->computedVirtualColumns)) {
- // Term not yet computed
- $formulaWithoutOperators .= $this->getDerivedFormulaWithoutOperators($this->virtualColumnsWithInfo[$term], $datumToModify);
- // $this->computedVirtualColumns[] = $term;
- } else {
- // Term already computed
- }
- }
- } else {
- $formulaWithoutOperators .= $term . " ";
- }
- }
- return $formulaWithoutOperators;
- }
- /**
- * @param $input
- * @return array
- */
- private function getKeys($input)
- {
- $keys = [];
- foreach ($input as $object)
- foreach ($object as $key => $val)
- if (!in_array($key, $keys)) $keys[] = $key;
- return $keys;
- }
- /**
- * @param $leftBrickData
- * @param $rightBrickData
- * @param $leftKey
- * @param $rightKey
- * @param $joinType
- * @return array
- */
- private function join($leftBrickSQL, $rightBrickSQL, $leftKey, $joinType, $rightColumns, $alias1, $alias2, $newRightKey)
- {
- return "SELECT * FROM ({$leftBrickSQL}) AS {$alias1} {$joinType} JOIN ( SELECT $rightColumns FROM ( {$rightBrickSQL}) AS {$alias2}) AS {$alias2} ON {$alias2}.{$newRightKey} = {$alias1}.{$leftKey}";
- }
- /**
- * @param $columnData
- */
- private function populateColumns($columnData)
- {
- foreach ($columnData as $column) {
- $attributes = $column['attributes'];
- $column = new Column();
- $properties = get_object_vars($column);
- # Populate column object with values from attributes array
- foreach ($properties as $property => $value) {
- if (array_key_exists($property, $attributes)) {
- $column->$property = isset($attributes[$property]) ? $attributes[$property] : NULL;
- }
- }
- $this->columns[] = $column;
- }
- }
- /**
- * @param $columnNames
- * @param $data
- * @return array
- */
- public function toTable($columnNames, $data)
- {
- $output = [];
- $columns = [];
- foreach ($columnNames as $name) {
- $columns[] = $name;
- }
- # Add columns key to the output
- $output['columns'] = $columns;
- # Create data key to hold the row values
- $output['data'] = [];
- # Populate rows
- $i = 0;
- foreach ($data as $values) {
- $row = [];
- foreach ($columnNames as $column) {
- if (array_key_exists($column, $values)) {
- $row[] = $values->{$column};
- } else {
- $row[] = '';
- }
- }
- $output['data'][] = $row;
- $i++;
- }
- return $output;
- }
- /**
- *
- */
- private function setColumnProperties()
- {
- $count = count($this->columns);
- if ($count === 0) {
- throw ValidationException::withMessages(["Empty Columns"]);
- }
- foreach ($this->columns as $column) {
- if (!empty($column->getName())) {
- $nameAttr = $column->getName();
- if ($column->getVisible() == 0) {
- $this->nonVisibleColumns[] = $nameAttr;
- } else {
- $this->visibleColumns[] = $nameAttr;
- }
- if ($column->getIsVirtual() == true) {
- $this->virtualColumnsWithInfo[$nameAttr] = $column->getComputedAs();
- }
- if (!empty($column->getTitle())) {
- $this->nameToTitleList[$nameAttr] = $column->getTitle();
- }
- }
- }
- }
- /**
- *
- */
- private function setFinalColumnNames()
- {
- if (!$this->nameToTitleList) {
- # No titles in any column
- $this->nameToTitleList = [];
- }
- $tmpColumnsList = array_merge($this->visibleColumns, array_values($this->nameToTitleList));
- foreach ($this->nameToTitleList as $name => $title) {
- if (($key = array_search($name, $this->visibleColumns)) !== false) {
- unset($tmpColumnsList[$key]);
- }
- }
- $this->finalColumnNames = $tmpColumnsList;
- }
- /**
- * @param $data
- * @param array $requiredColumns
- * @param bool $allColumns
- * @return mixed
- */
- private function filterData($data, $requiredColumns = [], $allColumns = false)
- {
- # Start with original data then start filtering
- $filteredData = $data;
- $i = 0;
- foreach ($filteredData as $columns) {
- $columns = (array)$columns;
- # Rename names to titles if present
- foreach ($this->nameToTitleList as $name => $title) {
- if ($this->checkKeyValid($name, $columns)) {
- $filteredData[$i]->{$title} = $filteredData[$i]->{$name};
- unset($filteredData[$i]->{$name});
- }
- }
- if (!$allColumns) {
- # Remove non visible columns
- foreach ($columns as $property => $value) {
- if (!in_array($property, $this->finalColumnNames)) {
- unset($filteredData[$i]->$property);
- }
- }
- }
- if (count($requiredColumns) > 0) {
- foreach ($filteredData as $val) {
- foreach ($val as $property => $value) {
- if (!in_array($property, $requiredColumns)) {
- if (!in_array($property, array_keys($this->virtualColumnsWithInfo))) {
- unset($filteredData[$i]->$property);
- }
- }
- }
- }
- }
- $i++;
- }
- return $filteredData;
- }
- /**
- * @param $key
- * @param $array
- * @return bool
- */
- private function checkKeyValid($key, $array)
- {
- if (array_key_exists($key, $array) && !empty($array[$key])) {
- return true;
- }
- return false;
- }
- private function getColumnTotal($data, $columnName) {
- $sum = 0;
- foreach ($data as $datum) {
- $sum += $datum->{$columnName};
- }
- return $sum;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement