Source of file CMSNicetiesEasyRelationshipField.php
Size: 17,756 Bytes - Last Modified: 2021-12-23T10:39:07+00:00
/var/www/docs.ssmods.com/process/src/src/Forms/CMSNicetiesEasyRelationshipField.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584 | <?php namespace Sunnysideup\CMSNiceties\Forms; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\CompositeField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\GridField\GridField; use SilverStripe\Forms\GridField\GridField_ActionMenu; use SilverStripe\Forms\GridField\GridFieldAddExistingAutocompleter; use SilverStripe\Forms\GridField\GridFieldAddNewButton; use SilverStripe\Forms\GridField\GridFieldConfig; use SilverStripe\Forms\GridField\GridFieldConfig_RelationEditor; use SilverStripe\Forms\GridField\GridFieldDataColumns; use SilverStripe\Forms\GridField\GridFieldDeleteAction; use SilverStripe\Forms\GridField\GridFieldDetailForm; use SilverStripe\Forms\HeaderField; use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataObject; use SilverStripe\Versioned\GridFieldArchiveAction; use SilverStripe\Versioned\Versioned; use SilverStripe\Versioned\VersionedGridFieldDetailForm; // use Symbiote\GridFieldExtensions\GridFieldOrderableRows; // TODO: use undefinedoffset/sortablegridfield /** * usage: * $fields->addFieldToTab( * 'Root.RelationFoo', * CMSNicetiesEasyRelationshipField::create($this, 'RelationFoo') * ->setSortField('SortOrder') * ->setLabelForField('Check this Out') * ->setHasEditRelation(true) * ->setHasUnlink(true) * ->setHasDelete(true) * ->setHasAdd(true) * ->setHasAddExisting(true) * ->setMaxItemsForCheckBoxSet(150) * ->setDataColumns(['Title' => 'My Title']) * ->setSearchFields(['Title' => 'My Title']) * ->setSearchOutputFormat('') * );. */ class CMSNicetiesEasyRelationshipField extends CompositeField { /** * the object calling this class, aka the class where we add the fields. * * @var object */ protected $callingObject; /** * name of the relations e.g. Members as defined in has_many or many_many. * * @var string */ protected $relationName = ''; /** * name of the class that we are linking to. * * @var string */ protected $relationClassName = ''; /** * name of the sort field used * works with: * - UndefinedOffset\SortableGridField\Forms\GridFieldSortableRows. * * @var string */ protected $sortField = ''; /** * @var array|string */ protected $checkBoxSort; /** * heading above field. * * @var string */ protected $labelForField = ''; /** * name for Add - e.g. My Product resulting in a button "Add My Product". * * @var string */ protected $addLabel = ''; /** * should the relationship be editable in the form? * * @var bool */ protected $hasEditRelation = true; /** * can the link be removed? * * @var bool */ protected $hasUnlink = true; /** * can the linked item be deleted? * * @var bool */ protected $hasDelete = true; /** * can new items be added? * * @var bool */ protected $hasAdd = true; /** * can existing items be linked? * * @var bool */ protected $hasAddExisting = true; /** * @var int */ protected $maxItemsForCheckBoxSet = 300; /** * data columns. * * @var array */ protected $dataColumns = []; /** * data columns. * * @var array */ protected $searchFields = []; /** * data columns. * * @var string */ protected $searchOutputFormat = ''; /** * @var null|GridFieldConfig */ private $gridFieldConfig; /** * @var null|GridField */ private $gridField; /** * @var null|CheckboxSetFieldWithLinks */ private $checkboxSetField; /** * @var null|DataList */ private $dataListForCheckboxSetField; /** * provides a generic Grid Field for Many Many relations. * * @param DataObject $callingObject Name of the Relationship - e.g. MyWidgets * @param string $relationName Name of the Relationship - e.g. MyWidgets * * @return array */ public function __construct($callingObject, $relationName) { $this->callingObject = $callingObject; $this->relationName = $relationName; parent::__construct(); $this->checkIfFieldsHaveBeenBuilt(); } public function doBuild($force = false) { if ($this->listIsEmpty() || $force) { $this->getChildren(); } } public function setSortField(string $sortField): self { $this->checkIfFieldsHaveBeenBuilt(); $this->sortField = $sortField; return $this; } public function setCheckBoxSort($sortArrayOrString): self { $this->checkIfFieldsHaveBeenBuilt(); $this->checkBoxSort = $sortArrayOrString; return $this; } public function setLabelForField(string $labelForField): self { $this->checkIfFieldsHaveBeenBuilt(); $this->labelForField = $labelForField; return $this; } public function setAddLabel(string $addLabel): self { $this->checkIfFieldsHaveBeenBuilt(); $this->addLabel = $addLabel; return $this; } public function setHasEditRelation(bool $hasEditRelation): self { $this->checkIfFieldsHaveBeenBuilt(); $this->hasEditRelation = $hasEditRelation; return $this; } public function setHasUnlink(bool $hasUnlink): self { $this->checkIfFieldsHaveBeenBuilt(); $this->hasUnlink = $hasUnlink; return $this; } public function setHasDelete(bool $hasDelete): self { $this->checkIfFieldsHaveBeenBuilt(); $this->hasDelete = $hasDelete; return $this; } public function setHasAdd(bool $hasAdd): self { $this->checkIfFieldsHaveBeenBuilt(); $this->hasAdd = $hasAdd; return $this; } public function setHasAddExisting(bool $hasAddExisting): self { $this->checkIfFieldsHaveBeenBuilt(); $this->hasAddExisting = $hasAddExisting; return $this; } public function setMaxItemsForCheckBoxSet(int $maxItemsForCheckBoxSet): self { $this->checkIfFieldsHaveBeenBuilt(); $this->maxItemsForCheckBoxSet = $maxItemsForCheckBoxSet; return $this; } public function setDataColumns(array $dataColumns): self { $this->checkIfFieldsHaveBeenBuilt(); $this->dataColumns = $dataColumns; return $this; } public function setSearchFields(array $searchFields): self { $this->checkIfFieldsHaveBeenBuilt(); $this->searchFields = $searchFields; return $this; } public function setSearchOutputFormat(string $searchOutputFormat): self { $this->searchOutputFormat = $searchOutputFormat; return $this; } public function setDataListForCheckboxSetField(DataList $list): self { $this->dataListForCheckboxSetField = $list; return $this; } public function getDetailedFields() { $this->doBuild(); $type = $this->getRelationClassName(); $obj = $type::create(); $defaultFields = $obj->getCMSFields(); $this->setDetailedFields($defaultFields); return $defaultFields; } public function setDetailedFields(FieldList $fields) { $this->doBuild(); $this->getDetailedForm()->setFields($fields); } /** * @return FieldList */ public function getChildren() { if ($this->listIsEmpty()) { $isVersioned = $this->isVersioned(); $hasCheckboxSet = $this->hasCheckboxSet(); $this->sortField = $this->getSortField(); $this->relationClassName = $this->getRelationClassName(); if ('' === $this->labelForField) { $fieldLabels = Injector::inst()->get($this->callingObject->ClassName)->fieldLabels(); $this->labelForField = $fieldLabels[$this->relationName] ?? $this->relationName; } $safeLabel = preg_replace('#[^A-Za-z0-9 ]#', '', $this->labelForField); $this->getGridFieldConfig = $this->getGridFieldConfig(); if ($hasCheckboxSet) { $this->hasUnlink = false; $this->hasAddExisting = false; } $this->gridField = null; if ($this->hasGridField()) { $this->getGridFieldConfig->removeComponentsByType(GridField_ActionMenu::class); $this->gridField = GridField::create( $this->relationName . 'GridField', $this->labelForField, $this->callingObject->{$this->relationName}(), $this->getGridFieldConfig ); //we remove both - just in case the type is unknown. $this->getGridFieldConfig->removeComponentsByType(GridFieldArchiveAction::class); $this->getGridFieldConfig->removeComponentsByType(GridFieldDeleteAction::class); //deletes if ($this->hasDelete) { if ($isVersioned) { // $this->getGridFieldConfig->addComponent(new GridFieldArchiveAction()); // $this->getGridFieldConfig->addComponent(new GridFieldDeleteAction($unlink = false)); } else { $this->getGridFieldConfig->addComponent(new GridFieldDeleteAction($unlink = false)); } } elseif ($this->hasUnlink) { $this->getGridFieldConfig->addComponent(new GridFieldDeleteAction($unlink = true)); } if ($this->hasAdd) { if ('' !== $this->addLabel) { $this->getGridFieldConfig->getComponentsByType(GridFieldAddNewButton::class)->first()->setButtonName('Add ' . $this->addLabel); } } else { $this->getGridFieldConfig->removeComponentsByType(GridFieldAddNewButton::class); } if (! $this->hasAddExisting) { $this->getGridFieldConfig->removeComponentsByType(GridFieldAddExistingAutocompleter::class); } if ($hasCheckboxSet) { $this->gridField->setTitle('Added ' . $this->labelForField); } if ('' !== $this->sortField) { $classA = 'UndefinedOffset\\SortableGridField\\Forms\\GridFieldSortableRows'; if (class_exists($classA)) { $this->getGridFieldConfig->addComponent($sorter = new $classA($this->sortField)); $sorter->setCustomRelationName($this->relationName); } } if (count($this->dataColumns) > 0) { $dataColumns = $this->getGridFieldConfig->getComponentByType(GridFieldDataColumns::class); if ($dataColumns) { $dataColumns->setDisplayFields($this->dataColumns); } } if (count($this->searchFields) > 0) { $autocompleter = $this->getGridFieldConfig->getComponentByType(GridFieldAddExistingAutocompleter::class); if ($autocompleter) { $autocompleter->setSearchFields($this->searchFields); } } if ('' !== $this->searchOutputFormat) { $autocompleter = $this->getGridFieldConfig->getComponentByType(GridFieldAddExistingAutocompleter::class); if ($autocompleter) { $autocompleter->setResultsFormat($this->searchOutputFormat); } } } $this->checkboxSetField = null; if ($hasCheckboxSet) { $className = $this->relationClassName; $obj = Injector::inst()->get($className); if (null === $this->dataListForCheckboxSetField) { $this->dataListForCheckboxSetField = $className::get(); } if ($this->dataListForCheckboxSetField && $this->checkBoxSort) { $this->dataListForCheckboxSetField = $this->dataListForCheckboxSetField->sort($this->checkBoxSort); } if ($obj->hasMethod('getTitleForList')) { $list = $this->dataListForCheckboxSetField->map('ID', 'getTitleForList'); } else { $list = $this->dataListForCheckboxSetField->map('ID', 'Title'); } $this->checkboxSetField = CheckboxSetFieldWithLinks::create( $this->relationName, 'Add / Remove', $list )->setClassNameForLinks($this->relationClassName); } $fieldsArray = [ HeaderField::create($safeLabel . 'Header', $this->labelForField, 1), ]; if (null !== $this->gridField) { $fieldsArray[] = $this->gridField; } if (null !== $this->checkboxSetField) { $fieldsArray[] = $this->checkboxSetField; } $this->children = FieldList::create($fieldsArray); //important - as setChildren does more than just setting variable... $this->setChildren($this->children); } return $this->children; } public function getGridFieldConfig() { if (null === $this->gridFieldConfig) { $this->gridFieldConfig = GridFieldConfig_RelationEditor::create(); } return $this->gridFieldConfig; } public function getGritField() { return $this->gridField; } public function getCheckboxSetField() { return $this->checkboxSetField; } protected function listIsEmpty(): bool { if (empty($this->children)) { return true; } return $this->children instanceof FieldList && ! $this->children->exists(); } protected function checkIfFieldsHaveBeenBuilt() { if ($this->listIsEmpty()) { //all good } else { user_error('There is an error in the sequence of your logic. The fields have already been built!'); } } /** * @return GridFieldDetailForm|VersionedGridFieldDetailForm */ protected function getDetailedForm() { $this->doBuild(); $this->getGridFieldConfig = $this->getGridFieldConfig(); return $this->getGridFieldConfig->getComponentByType(GridFieldDetailForm::class); } private function getRelationClassName(): string { if ('' === $this->relationClassName) { $hasMany = Config::inst()->get($this->callingObject->ClassName, 'has_many'); $manyMany = Config::inst()->get($this->callingObject->ClassName, 'many_many'); $belongsManyMany = Config::inst()->get($this->callingObject->ClassName, 'belongs_many_many'); foreach ([ $hasMany, $manyMany, $belongsManyMany, ] as $types) { if (isset($types[$this->relationName])) { $typeOptions = $types[$this->relationName]; $typeArray = explode('.', $typeOptions); $this->relationClassName = $typeArray[0]; break; } } } if ($this->relationClassName && class_exists($this->relationClassName)) { return $this->relationClassName; } user_error('Can not find related class: ' . $this->relationClassName); return 'error'; } private function isVersioned(): bool { $this->relationClassName = $this->getRelationClassName(); $foreignSingleton = Injector::inst()->get($this->relationClassName); return (bool) $foreignSingleton->hasExtension(Versioned::class); } private function hasCheckboxSet(): bool { if ($this->callingObject->canEdit()) { $this->relationClassName = $this->getRelationClassName(); $className = $this->relationClassName; return $className::get()->count() < $this->maxItemsForCheckBoxSet; } return false; } private function hasGridField(): bool { //do we need it to edit the relationship? if ($this->hasEditRelation || $this->hasDelete || $this->hasAdd || $this->sortField) { return true; } // do we need it because we do not have a checkboxset? //we can go without! return ! $this->hasCheckboxSet(); } private function getSortField(): string { //todo - add undefinedoffset/sortablegridfield if ('' === $this->sortField) { $manyManyExtras = Config::inst()->get($this->callingObject->ClassName, 'many_many_extraFields'); if (isset($manyManyExtras[$this->relationName])) { foreach ($manyManyExtras[$this->relationName] as $field => $tempType) { if ('int' === strtolower($tempType)) { $this->sortField = $field; } } } } return $this->sortField; } } |