diff --git a/README.md b/README.md index 294c8b6..01893da 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ This module provides an easy way to build such tree and to add it to a page as a ## Requirements -* SilverStripe 5.x +* SilverStripe 6.x * (dnadesign/silvertsripe-elemental)[https://github.com/dnadesign/silverstripe-elemental] ## Screenshots diff --git a/composer.json b/composer.json index bec508f..456cf72 100644 --- a/composer.json +++ b/composer.json @@ -17,9 +17,9 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "dnadesign/silverstripe-elemental": "^5", - "silvershop/silverstripe-hasonefield": "^4", - "unclecheese/display-logic": "^3" + "dnadesign/silverstripe-elemental": "^6", + "silvershop/silverstripe-hasonefield": "^5", + "unclecheese/display-logic": "^4" }, "config": { "allow-plugins": { diff --git a/src/Extensions/ElementDecisionTreeController.php b/src/Extensions/ElementDecisionTreeController.php index 4e4e4eb..bbffce7 100644 --- a/src/Extensions/ElementDecisionTreeController.php +++ b/src/Extensions/ElementDecisionTreeController.php @@ -2,34 +2,33 @@ namespace DNADesign\SilverStripeElementalDecisionTree\Extensions; +use DNADesign\SilverStripeElementalDecisionTree\Model\DecisionTreeAnswer; +use DNADesign\SilverStripeElementalDecisionTree\Model\DecisionTreeStep; use SilverStripe\Control\Controller; -use SilverStripe\View\ArrayData; -use SilverStripe\View\Requirements; use SilverStripe\Core\Extension; -use DNADesign\SilverStripeElementalDecisionTree\Model\DecisionTreeStep; -use DNADesign\SilverStripeElementalDecisionTree\Model\DecisionTreeAnswer; +use SilverStripe\Model\ArrayData; +use SilverStripe\ORM\FieldType\DBHTMLText; +use SilverStripe\View\Requirements; class ElementDecisionTreeController extends Extension { - - private static $allowed_actions = [ - 'getNextStepForAnswer' + private static array $allowed_actions = [ + 'getNextStepForAnswer', ]; - public function onAfterInit() + public function onAfterInit(): void { Requirements::javascript('dnadesign/silverstripe-elemental-decisiontree:javascript/decision-tree.src.js'); } /** - * Return the HTMl for the next step to be displayed - * as well as the updated URL which includes the ids of the answers - * leading to this next step to be returned - * - * @param stepanswerid (POST) - * @return json - */ - public function getNextStepForAnswer() + * Return the HTMl for the next step to be displayed + * as well as the updated URL which includes the ids of the answers + * leading to this next step to be returned. + * + * @param stepanswerid (POST) + */ + public function getNextStepForAnswer(): null|bool|string|DBHTMLText { $answerID = $this->owner->getRequest()->postVar('stepanswerid'); @@ -41,7 +40,7 @@ public function getNextStepForAnswer() if (!$answer || !$answer->exists()) { return $this->owner->httpError( - 404, + 404, $this->renderError('An error has occurred, please reload the page and try again!') ); } @@ -50,26 +49,27 @@ public function getNextStepForAnswer() if (!$nextStep || !$nextStep->exists()) { return $this->owner->httpError( - 404, + 404, $this->renderError('An error has occurred, please reload the page and try again!') ); } $html = $this->owner->customise(new ArrayData([ 'Step' => $nextStep, - 'Controller' => $this->owner + 'Controller' => $this->owner, ]))->renderWith('DNADesign\SilverStripeElementalDecisionTree\Model\DecisionTreeStep'); $pathway = $nextStep->getAnswerPathway(); $nextURL = Controller::join_links( - $this->owner->AbsoluteLink(), '?decisionpathway='.implode(',', $pathway) + $this->owner->AbsoluteLink(), + '?decisionpathway=' . implode(',', $pathway) ); if ($this->owner->getRequest()->isAjax()) { $data = [ 'html' => $html->forTemplate(), - 'nexturl' => $nextURL + 'nexturl' => $nextURL, ]; return json_encode($data); @@ -79,12 +79,10 @@ public function getNextStepForAnswer() } /** - * Returns an array of DecisionStepID from the URL param - * in order to display the same question when we reload the page - * - * @return Array - */ - public function getInitialPathway() + * Returns an array of DecisionStepID from the URL param + * in order to display the same question when we reload the page. + */ + public function getInitialPathway(): ?array { $ids = $this->owner->getRequest()->getVar('decisionpathway'); @@ -96,12 +94,12 @@ public function getInitialPathway() } /** - * Check if an answer should be selected by default - * ie. The question depending on it is displayed - * - * @return Boolean - */ - public function getIsAnswerSelected($answerID) + * Check if an answer should be selected by default + * ie. The question depending on it is displayed. + * + * @param mixed $answerID + */ + public function getIsAnswerSelected($answerID): bool { if ($pathway = $this->getInitialPathway()) { return in_array($answerID, $pathway); @@ -111,16 +109,16 @@ public function getIsAnswerSelected($answerID) } /** - * Gets the next step to be displayed in regards to the selected answer. - * Used by template to display all the relevant steps from the URL - * - * @return DecisionTreeStep - */ - public function getNextStepFromSelectedAnswer($stepID) + * Gets the next step to be displayed in regards to the selected answer. + * Used by template to display all the relevant steps from the URL. + * + * @param mixed $stepID + */ + public function getNextStepFromSelectedAnswer($stepID): ?DecisionTreeStep { $step = DecisionTreeStep::get()->byID($stepID); if ($step->exists()) { - foreach($step->Answers() as $answer) { + foreach ($step->Answers() as $answer) { if ($this->getIsAnswerSelected($answer->ID)) { if ($nextStep = $answer->ResultingStep()) { return $nextStep; @@ -133,13 +131,9 @@ public function getNextStepFromSelectedAnswer($stepID) } /** - * Template returned via ajax in case of an error occuring. - * - * @param string - * - * @return string - */ - protected function renderError($message = '') + * Template returned via ajax in case of an error occuring. + */ + protected function renderError(string $message = ''): string { return sprintf( '
@@ -148,9 +142,8 @@ protected function renderError($message = '') Sorry!

%s

- ', + ', $message ); } - } diff --git a/src/Forms/DecisionTreeStepPreview.php b/src/Forms/DecisionTreeStepPreview.php index 997e625..edf2e3f 100644 --- a/src/Forms/DecisionTreeStepPreview.php +++ b/src/Forms/DecisionTreeStepPreview.php @@ -2,6 +2,7 @@ namespace DNADesign\SilverStripeElementalDecisionTree\Forms; +use DNADesign\SilverStripeElementalDecisionTree\Model\DecisionTreeStep; use SilverStripe\Forms\FormField; /** @@ -9,23 +10,22 @@ * ElementDecisionTree or DecisionTreeStes. It provides a visual way of * navigating the tree as well as links to edit/add steps. */ - class DecisionTreeStepPreview extends FormField { - protected $step = null; + protected ?DecisionTreeStep $step = null; - public function __construct($name, $step = null) + public function __construct($name, ?DecisionTreeStep $step = null) { $this->step = $step; parent::__construct($name); } - public function getStep() + public function getStep(): ?DecisionTreeStep { return $this->step; } - public function setStep($step) + public function setStep(?DecisionTreeStep $step): static { $this->step = $step; diff --git a/src/Forms/HasOneSelectOrCreateField.php b/src/Forms/HasOneSelectOrCreateField.php index 8038e26..83be76d 100644 --- a/src/Forms/HasOneSelectOrCreateField.php +++ b/src/Forms/HasOneSelectOrCreateField.php @@ -2,32 +2,31 @@ namespace DNADesign\SilverStripeElementalDecisionTree\Forms; +use SilverShop\HasOneField\GridFieldHasOneEditButton; +use SilverShop\HasOneField\HasOneButtonField; use SilverStripe\Forms\CompositeField; use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\FormField; -use SilverShop\HasOneField\HasOneButtonField; -use SilverShop\HasOneField\GridFieldHasOneEditButton; -/** -* This a wrapper for a slightly enhanced user experience when using the -* HasOneButtonField class. -* By default, the HasOneButtonField only allow to create or edit the has_one object -* This class adds a dropdown to allow to select another object or remove the relation -* without deleting the object itself. -* Both fields are wrapped in a CompositeField. -* -* @param String | Name of the has_one relationship -* @param String | Title of the drodpwon field -* @param FieldList or Array | Options listed in the dropdown -* @param Object | Current has_one object, if exists -* @param Object | The object calling this field, required by HasOneButtonField -*/ +/** + * This a wrapper for a slightly enhanced user experience when using the + * HasOneButtonField class. + * By default, the HasOneButtonField only allow to create or edit the has_one object + * This class adds a dropdown to allow to select another object or remove the relation + * without deleting the object itself. + * Both fields are wrapped in a CompositeField. + * + * @param Name|string of the has_one relationship + * @param string|Title of the drodpwon field + * @param FieldList or Array | Options listed in the dropdown + * @param Current|object has_one object, if exists + * @param object|The object calling this field, required by HasOneButtonField + */ class HasOneSelectOrCreateField extends CompositeField { + protected DropdownField $dropdown; - protected $dropdown; - - protected $gridfield; + protected HasOneButtonField $gridfield; public function __construct($record, $name, $title, $options, $current, $parent) { @@ -49,21 +48,20 @@ public function __construct($record, $name, $title, $options, $current, $parent) if ($current && $current->exists()) { $name = ($current->Title) ? $current->Title : $current->Name; if ($name) { - $button->setButtonName('Edit '.FormField::name_to_label($name)); + $button->setButtonName('Edit ' . FormField::name_to_label($name)); } } else { - $button->setButtonName('Create '.$label); + $button->setButtonName('Create ' . $label); } // Set Empty String on dropdown - $dropdown->setEmptyString('Select '.$label); + $dropdown->setEmptyString('Select ' . $label); - parent::__construct(array($this->dropdown, $this->gridfield)); + parent::__construct([$this->dropdown, $this->gridfield]); } - public function getRelationName() + public function getRelationName(): string { - return $this->name.'ID'; + return $this->name . 'ID'; } - } diff --git a/src/Model/DecisionTreeAnswer.php b/src/Model/DecisionTreeAnswer.php index cf0a097..5821688 100644 --- a/src/Model/DecisionTreeAnswer.php +++ b/src/Model/DecisionTreeAnswer.php @@ -2,32 +2,32 @@ namespace DNADesign\SilverStripeElementalDecisionTree\Model; -use SilverStripe\ORM\DataObject; +use DNADesign\SilverStripeElementalDecisionTree\Forms\HasOneSelectOrCreateField; use SilverStripe\Control\Controller; use SilverStripe\Forms\LiteralField; -use DNADesign\SilverStripeElementalDecisionTree\Forms\HasOneSelectOrCreateField; +use SilverStripe\ORM\DataObject; class DecisionTreeAnswer extends DataObject { - private static $db = [ + private static array $db = [ 'Title' => 'Varchar(255)', - 'Sort' => 'Int' + 'Sort' => 'Int', ]; - private static $has_one = [ + private static array $has_one = [ 'Question' => DecisionTreeStep::class, - 'ResultingStep' => DecisionTreeStep::class + 'ResultingStep' => DecisionTreeStep::class, ]; - private static $summary_fields = [ + private static array $summary_fields = [ 'ID' => 'ID', 'Title' => 'Title', - 'ResultingStep.Title' => 'Resulting Step' + 'ResultingStep.Title' => 'Resulting Step', ]; - private static $table_name = 'DecisionTreeAnswer'; + private static string $table_name = 'DecisionTreeAnswer'; - private static $default_sort = 'Sort ASC'; + private static string $default_sort = 'Sort ASC'; public function getCMSFields() { @@ -55,10 +55,15 @@ public function getCMSFields() } $stepSelector = HasOneSelectOrCreateField::create( - $this, 'ResultingStep', 'If selected, go to', $steps, $this->ResultingStep(), $this + $this, + 'ResultingStep', + 'If selected, go to', + $steps, + $this->ResultingStep(), + $this ); - $fields->addFieldsToTab('Root.Main', $stepSelector); + $fields->addFieldToTab('Root.Main', $stepSelector); } else { $info = LiteralField::create('info', sprintf( '

%s

', @@ -87,38 +92,38 @@ public function canEdit($member = null) } /** - * Can only delete an answer that doesn't have a dependant question + * Can only delete an answer that doesn't have a dependant question. + * + * @param null|mixed $member */ public function canDelete($member = null) { $canDelete = singleton(ElementDecisionTree::class)->canDelete($member); - return ($canDelete && !$this->ResultingStep()->exists()); + return $canDelete && !$this->ResultingStep()->exists(); } /** - * Used as breadcrumbs on the parent Step - * - * @return String - */ - public function TitleWithQuestion() + * Used as breadcrumbs on the parent Step. + */ + public function TitleWithQuestion(): ?string { $title = $this->Title; if ($this->Question()->exists()) { $title = sprintf('%s > %s', $this->Question()->Title, $title); } + return $title; } /** - * Create a link that allowd to edit this object in the CMS - * To do this, it first finds its parent question - * then rewind the tree up to the element - * then append its edit url to the edit url of its parent question - * - * @return String - */ - public function CMSEditLink() { + * Create a link that allowd to edit this object in the CMS + * To do this, it first finds its parent question + * then rewind the tree up to the element + * then append its edit url to the edit url of its parent question. + */ + public function getCMSEditLink(): ?string + { if ($this->Question()->exists()) { $origin = $this->Question()->getTreeOrigin(); @@ -126,39 +131,33 @@ public function CMSEditLink() { $root = $origin->ParentElement(); if ($root) { - $url = Controller::join_links( + return Controller::join_links( $root->CMSEditFirstStepLink(), $this->Question()->getRecursiveEditPath(), $this->getRecursiveEditPathForSelf() ); - - return $url; } } } + + return parent::getCMSEditLink(); } /** - * Construct the link tp create a new ResultingStep for this answer - * - * @return String - */ - public function CMSAddStepLink() + * Construct the link tp create a new ResultingStep for this answer. + */ + public function CMSAddStepLink(): string { - $link = Controller::join_links( - $this->CMSEditLink(), + return Controller::join_links( + $this->getCMSEditLink(), 'itemEditForm/field/ResultingStep/item/new' ); - - return $link; } /** - * Recursively construct the link to edit this object - * - * @return String - */ - public function getRecursiveEditPath() + * Recursively construct the link to edit this object. + */ + public function getRecursiveEditPath(): string { $path = sprintf('ItemEditForm/field/Answers/item/%s/', $this->ID); @@ -173,11 +172,9 @@ public function getRecursiveEditPath() } /** - * Return only the url segment to edit this object - * - * @return String - */ - public function getRecursiveEditPathForSelf() + * Return only the url segment to edit this object. + */ + public function getRecursiveEditPathForSelf(): string { return sprintf('ItemEditForm/field/Answers/item/%s/', $this->ID); } diff --git a/src/Model/DecisionTreeStep.php b/src/Model/DecisionTreeStep.php index ebe135a..9da73bd 100644 --- a/src/Model/DecisionTreeStep.php +++ b/src/Model/DecisionTreeStep.php @@ -2,53 +2,55 @@ namespace DNADesign\SilverStripeElementalDecisionTree\Model; -use SilverStripe\ORM\DataObject; -use SilverStripe\Forms\CheckboxField; -use SilverStripe\ORM\ArrayList; -use SilverStripe\ORM\FieldType\DBField; +use DNADesign\SilverStripeElementalDecisionTree\Forms\DecisionTreeStepPreview; use SilverStripe\Control\Controller; -use SilverStripe\Forms\ReadOnlyField; +use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\GridField\GridField; -use SilverStripe\Forms\OptionsetField; use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor; +use SilverStripe\Forms\OptionsetField; +use SilverStripe\Forms\ReadOnlyField; +use SilverStripe\Model\List\ArrayList; +use SilverStripe\Model\List\SS_List; +use SilverStripe\ORM\DataObject; +use SilverStripe\ORM\FieldType\DBField; +use SilverStripe\ORM\FieldType\DBHTMLText; use Symbiote\GridFieldExtensions\GridFieldOrderableRows; use UncleCheese\DisplayLogic\Forms\Wrapper as DisplayLogicWrapper; -use DNADesign\SilverStripeElementalDecisionTree\Forms\DecisionTreeStepPreview; class DecisionTreeStep extends DataObject { - private static $db = [ + private static array $db = [ 'Title' => 'Varchar(255)', 'Type' => "Enum('Question, Result')", 'Content' => 'HTMLText', - 'HideTitle' => 'Boolean' + 'HideTitle' => 'Boolean', ]; - private static $has_many = [ - 'Answers' => DecisionTreeAnswer::class.'.Question' + private static array $has_many = [ + 'Answers' => DecisionTreeAnswer::class . '.Question', ]; - private static $owns = [ - 'Answers' + private static array $owns = [ + 'Answers', ]; - private static $cascade_deletes = [ - 'Answers' + private static array $cascade_deletes = [ + 'Answers', ]; - private static $table_name = 'DecisionTreeStep'; + private static string $table_name = 'DecisionTreeStep'; - private static $belongs_to = [ + private static array $belongs_to = [ 'ParentAnswer' => DecisionTreeAnswer::class, - 'ParentElement' => ElementDecisionTree::class + 'ParentElement' => ElementDecisionTree::class, ]; - private static $summary_fields = [ + private static array $summary_fields = [ 'ID' => 'ID', - 'Title' => 'Title' + 'Title' => 'Title', ]; - private static $default_result_title = 'Our recommendation'; + private static string $default_result_title = 'Our recommendation'; public function getCMSFields() { @@ -59,7 +61,7 @@ public function getCMSFields() $fields->removeByName('Answers'); - $fields->replaceField('Type', $type = OptionsetField::create('Type', 'Type' ,$this->dbObject('Type')->enumValues())); + $fields->replaceField('Type', $type = OptionsetField::create('Type', 'Type', $this->dbObject('Type')->enumValues())); // Allow to hide the title only on Result $hideTitle = CheckboxField::create('HideTitle', 'Hide title'); @@ -94,9 +96,9 @@ public function getCMSFields() } /** - * Set default title on Result steps - */ - public function onBeforeWrite() + * Set default title on Result steps. + */ + public function onBeforeWrite(): void { if ($this->Type == 'Result' && !$this->Title) { $this->Title = $this->config()->default_result_title; @@ -122,26 +124,22 @@ public function canEdit($member = null) public function canDelete($member = null) { - $canDelete = singleton(ElementDecisionTree::class)->canDelete($member); - - return $canDelete; + return singleton(ElementDecisionTree::class)->canDelete($member); } /** - * Return a readable list of the answer title and the title of the question - * which will be displayed if the answer is selected - * Used for Gridfield - * - * @return HTMLText - */ - public function getAnswerTreeForGrid() + * Return a readable list of the answer title and the title of the question + * which will be displayed if the answer is selected + * Used for Gridfield. + */ + public function getAnswerTreeForGrid(): DBField|DBHTMLText { $output = ''; if ($this->Answers()->Count()) { - foreach($this->Answers() as $answer) { + foreach ($this->Answers() as $answer) { $output .= $answer->Title; if ($answer->ResultingStep()) { - $output .= ' => '.$answer->ResultingStep()->Title; + $output .= ' => ' . $answer->ResultingStep()->Title; } $output .= '
'; } @@ -151,14 +149,12 @@ public function getAnswerTreeForGrid() } /** - * Outputs an optionset to allow user to select an answer to the question - * - * @return OptionsetField - */ - public function getAnswersOptionset() + * Outputs an optionset to allow user to select an answer to the question. + */ + public function getAnswersOptionset(): OptionsetField { - $source = array(); - foreach($this->Answers() as $answer) { + $source = []; + foreach ($this->Answers() as $answer) { $source[$answer->ID] = $answer->Title; } @@ -166,22 +162,20 @@ public function getAnswersOptionset() } /** - * Return the DecisionAnswer rsponsible for displaying this step - * - * @return DecisionTreeAnswer - */ - public function getParentAnswer() + * Return the DecisionAnswer rsponsible for displaying this step. + */ + public function getParentAnswer(): ?DecisionTreeAnswer { - return DecisionTreeAnswer::get()->filter('ResultingStepID', $this->ID)->First(); + return DecisionTreeAnswer::get()->filter('ResultingStepID', $this->ID)->first(); } /** - * Return the list of DecisionTreeAnswer ID - * leading to this step being displayed - * - * @return Array - */ - public function getAnswerPathway(&$idList = array()) + * Return the list of DecisionTreeAnswer ID + * leading to this step being displayed. + * + * @param mixed $idList + */ + public function getAnswerPathway(&$idList = []): array { if ($answer = $this->getParentAnswer()) { array_push($idList, $answer->ID); @@ -194,12 +188,12 @@ public function getAnswerPathway(&$idList = array()) } /** - * Return the list of DecisionTreeStep ID - * leading to this step being displayed - * - * @return Array - */ - public function getQuestionPathway(&$idList = array()) + * Return the list of DecisionTreeStep ID + * leading to this step being displayed. + * + * @param mixed $idList + */ + public function getQuestionPathway(&$idList = []): array { array_push($idList, $this->ID); if ($answer = $this->getParentAnswer()) { @@ -212,46 +206,43 @@ public function getQuestionPathway(&$idList = array()) } /** - * Builds an array of question and answers leading to this Step - * Each entry is an array which key is either 'question' or 'answer' - * and value is the ID of the object - * Note: the array is in reverse order - * - * @return Array - */ - public function getFullPathway(&$path = array()) + * Builds an array of question and answers leading to this Step + * Each entry is an array which key is either 'question' or 'answer' + * and value is the ID of the object + * Note: the array is in reverse order. + * + * @param mixed $path + */ + public function getFullPathway(&$path = []): array { if ($answer = $this->getParentAnswer()) { - array_push($path, array('question' => $this->ID)); - array_push($path, array('answer' => $answer->ID)); + array_push($path, ['question' => $this->ID]); + array_push($path, ['answer' => $answer->ID]); if ($question = $answer->Question()) { $question->getFullPathway($path); } } else { - array_push($path, array('question' => $this->ID)); + array_push($path, ['question' => $this->ID]); } return $path; } /** - * Find the very first DecisionStep in the tree - * - * @return DecisionTreeStep - */ - public function getTreeOrigin() + * Find the very first DecisionStep in the tree. + */ + public function getTreeOrigin(): ?DecisionTreeStep { $pathway = array_reverse($this->getQuestionPathway()); + return DecisionTreeStep::get()->byID($pathway[0]); } /** - * Return this step position in the pathway - * Used to number step on the front end - * - * @return Int - */ - public function getPositionInPathway() + * Return this step position in the pathway + * Used to number step on the front end. + */ + public function getPositionInPathway(): int { $pathway = array_reverse($this->getFullPathway()); // Pathway has both questions and answers @@ -264,13 +255,11 @@ public function getPositionInPathway() } /** - * Return a DataList of DecisionTreeStep that do not belong to a Tree - * - * @return SS_List - */ - public static function get_orphans() + * Return a DataList of DecisionTreeStep that do not belong to a Tree. + */ + public static function get_orphans(): SS_List { - $orphans = DecisionTreeStep::get()->filterByCallback(function($item) { + $orphans = DecisionTreeStep::get()->filterByCallback(function ($item) { return !$item->belongsToTree(); }); @@ -282,14 +271,12 @@ public static function get_orphans() } /** - * Return a DataList of all DecisionTreeStep that do not belong to an answer - * ie. are the first child of a element - * - * @return SS_List - */ - public static function get_initial_steps() + * Return a DataList of all DecisionTreeStep that do not belong to an answer + * ie. are the first child of a element. + */ + public static function get_initial_steps(): ?SS_List { - $initial = DecisionTreeStep::get()->filterByCallback(function($item) { + $initial = DecisionTreeStep::get()->filterByCallback(function ($item) { return !$item->belongsToAnswer(); }); @@ -298,41 +285,30 @@ public static function get_initial_steps() } return DecisionTreeStep::get()->filter([ - 'ID' => $initial->column('ID') + 'ID' => $initial->column('ID'), ])->exclude('Type', 'Result'); } - /** - * - */ - public function belongsToTree() + public function belongsToTree(): bool { - return ($this->belongsToElement() || $this->belongsToAnswer()); + return $this->belongsToElement() || $this->belongsToAnswer(); } - /** - * - */ - public function belongsToElement() + public function belongsToElement(): bool { - return (ElementDecisionTree::get()->filter('FirstStepID', $this->ID)->Count() > 0); + return ElementDecisionTree::get()->filter('FirstStepID', $this->ID)->Count() > 0; } - /** - * - */ - public function belongsToAnswer() + public function belongsToAnswer(): bool { - return ($this->ParentAnswer() && $this->ParentAnswer()->exists()); + return $this->ParentAnswer() && $this->ParentAnswer()->exists(); } /** - * Checks if this object is currently being edited in the CMS - * by comparing its ID with the one in the request - * - * @return Boolean - */ - public function IsCurrentlyEdited() + * Checks if this object is currently being edited in the CMS + * by comparing its ID with the one in the request. + */ + public function IsCurrentlyEdited(): bool { $request = Controller::curr()->getRequest(); $class = $request->param('FieldName'); @@ -341,50 +317,48 @@ public function IsCurrentlyEdited() $stepRelationships = ['ResultingStep', 'FirstStep']; if ($currentID && in_array($class, $stepRelationships)) { - return $currentID == $this->ID; + return $currentID == $this->ID; } return false; } /** - * Create a link that allowd to edit this object in the CMS - * To do this, it rewinds the tree up to the element - * then append its edit url to the edit url of its parent question - * - * @return String - */ - public function CMSEditLink() { + * Create a link that allowd to edit this object in the CMS + * To do this, it rewinds the tree up to the element + * then append its edit url to the edit url of its parent question. + */ + public function getCMSEditLink(): ?string + { $origin = $this->getTreeOrigin(); if ($origin) { $root = $origin->ParentElement(); if ($root) { - $url = Controller::join_links($root->CMSEditFirstStepLink(), $this->getRecursiveEditPath()); - return $url; + return Controller::join_links($root->CMSEditFirstStepLink(), $this->getRecursiveEditPath()); } } + + return parent::getCMSEditLink(); } /** - * Build url to allow to edit this object - * - * @return String - */ - public function getRecursiveEditPath() + * Build url to allow to edit this object. + */ + public function getRecursiveEditPath(): string { $pathway = array_reverse($this->getFullPathway()); unset($pathway[0]); // remove first question $url = ''; - foreach($pathway as $step) { + foreach ($pathway as $step) { if (is_array($step) && !empty($step)) { $type = array_keys($step)[0]; $id = $step[$type]; if ($type == 'question') { - $url .= '/ItemEditForm/field/ResultingStep/item/'.$id; - } else if ($type == 'answer') { - $url .= '/ItemEditForm/field/Answers/item/'.$id; + $url .= '/ItemEditForm/field/ResultingStep/item/' . $id; + } elseif ($type == 'answer') { + $url .= '/ItemEditForm/field/Answers/item/' . $id; } } } diff --git a/src/Model/ElementDecisionTree.php b/src/Model/ElementDecisionTree.php index 0ba6ac5..8e025e5 100644 --- a/src/Model/ElementDecisionTree.php +++ b/src/Model/ElementDecisionTree.php @@ -3,33 +3,33 @@ namespace DNADesign\SilverStripeElementalDecisionTree\Model; use DNADesign\Elemental\Models\BaseElement; -use DNADesign\SilverStripeElementalDecisionTree\Forms\HasOneSelectOrCreateField; use DNADesign\SilverStripeElementalDecisionTree\Forms\DecisionTreeStepPreview; -use SilverStripe\Control\Controller; +use DNADesign\SilverStripeElementalDecisionTree\Forms\HasOneSelectOrCreateField; use SilverStripe\CMS\Controllers\CMSPageEditController; +use SilverStripe\Control\Controller; use SilverStripe\Forms\LiteralField; class ElementDecisionTree extends BaseElement { - private static $title = "Decision Tree"; + private static string $title = 'Decision Tree'; - private static $description = "Display a decision tree with questions and results"; + private static string $class_description = 'Display a decision tree with questions and results'; - private static $enable_title_in_template = true; + private static bool $enable_title_in_template = true; - private static $icon = 'font-icon-flow-tree'; + private static string $icon = 'font-icon-flow-tree'; - private static $db = [ - 'Introduction' => 'HTMLText' + private static array $db = [ + 'Introduction' => 'HTMLText', ]; - private static $has_one = [ - 'FirstStep' => DecisionTreeStep::class + private static array $has_one = [ + 'FirstStep' => DecisionTreeStep::class, ]; - private static $table_name = 'ElementDecisionTree'; + private static string $table_name = 'ElementDecisionTree'; - private static $inline_editable = false; + private static bool $inline_editable = false; public function getType() { @@ -46,7 +46,12 @@ public function getCMSFields() if ($this->IsInDB()) { $stepSelector = HasOneSelectOrCreateField::create( - $this, 'FirstStep', 'First Step', DecisionTreeStep::get_initial_steps()->map(), $this->FirstStep(), $this + $this, + 'FirstStep', + 'First Step', + DecisionTreeStep::get_initial_steps()->map(), + $this->FirstStep(), + $this ); $fields->addFieldToTab('Root.Main', $stepSelector); @@ -65,16 +70,16 @@ public function getCMSFields() } /** - * Builds the Edit Link to the FirstStep of this element - * - * @return string - */ - public function CMSEditFirstStepLink() + * Builds the Edit Link to the FirstStep of this element. + */ + public function CMSEditFirstStepLink(): ?string { $page = $this->getPage(); $firstStep = $this->FirstStep(); - if (!$page || !$page->exists() || !$firstStep->exists()) return null; + if (!$page || !$page->exists() || !$firstStep->exists()) { + return null; + } return Controller::join_links( singleton(CMSPageEditController::class)->Link('EditForm'),