File: D:/HostingSpaces/SBogers10/bomacon.komma.pro/app/Components/Attributables/AttributableService.php
<?php
namespace App\Components\Attributables;
use App\Attributes\ComponentArea;
use App\Components\Component\Component;
use App\Components\Component\ComponentAttributeKey;
use App\Components\Component\ComponentSaveState;
use App\Components\ComponentType\ComponentTypeFactory;
use App\Components\ComponentType\Types\AbstractComponentType;
use Illuminate\Support\Facades\Request;
use Komma\KMS\Core\Attributes\Attribute;
use Komma\KMS\Core\Attributes\MultiSelect;
use Komma\KMS\Core\Sections\AttributeKey;
/**
* Class AttributableableService
*
* Knows which models (Attributables) are linked to specific component attributes.
* So that it can save and load those models to these components
*
* @package App\Components\Component
*/
class AttributableService implements AttributableServiceInterface
{
/**
* @param ComponentArea $componentArea
* @param AbstractComponentType $componentType
* @param int $component_id_before_save
* @param int $component_id_after_save
* @throws \Throwable
*/
public function saveAttributablesForComponentAttributes(ComponentArea $componentArea, AbstractComponentType $componentType, int $component_id_before_save, int $component_id_after_save)
{
foreach($componentType->getAttributes() as $attribute) {
//Check Autocomplete inputs which "canBeLinked" to other models.
if(is_a($attribute, MultiSelect::class)) {
/** @var MultiSelect $attribute */
if ($attribute->isFilledByProvider() === false) {
continue;
}
$canBeLinkedWith = $attribute->getCanBeLinkedWith();
if ($canBeLinkedWith === '') continue;
//Create the attribute key as it existed before saving the component and after saving
$componentAreaKey = $componentArea->getKey();
$componentAttributeKeyBeforeSave = ComponentAttributeKey::createInstance(
$component_id_before_save,
$attribute->getsValueFromReference(),
AttributeKey::getAttributeShortClassNameFromString($componentAreaKey),
AttributeKey::getValuePartFromString($componentAreaKey),
AttributeKey::getTranslationISO2FromString($componentAreaKey)
);
$componentAttributeKeyAfterSave = ComponentAttributeKey::createInstance(
$component_id_after_save,
$attribute->getsValueFromReference(),
AttributeKey::getAttributeShortClassNameFromString($componentAreaKey),
AttributeKey::getValuePartFromString($componentAreaKey),
AttributeKey::getTranslationISO2FromString($componentAreaKey)
);
$modelsIdsToLink = Request::get($componentAttributeKeyBeforeSave); //And use the before save key to get the autocomplete input id's that where selected.
//Convert the model ids to link (example: "1,2,4") to an array (example [1,2,4])
if($modelsIdsToLink) {
$ids = explode(',', $modelsIdsToLink);
$ids = array_map(function ($item) {
return (int)$item;
}, $ids); //convert the ids to ints
} else {
$ids = [];
}
\DB::transaction(function () use($componentAttributeKeyAfterSave, $canBeLinkedWith, $ids) {
//Delete attributables that don't have an attributable_id that is in the $ids array.
Attributable::where('component_attribute_key', '=', $componentAttributeKeyAfterSave)
->where('attributable_type', '=', $canBeLinkedWith)
->whereNotIn('attributable_id', $ids)->delete();
//Save new attributables. Leave existing ones
foreach($ids as $id) {
$attributeable = Attributable::firstOrNew([
'component_attribute_key' => (string)$componentAttributeKeyAfterSave,
'attributable_type' => $canBeLinkedWith,
'attributable_id' => $id
]);
$attributeable->save();
}
});
}
}
}
public static function deleteOrphanedComponentsAttributables(): void {
$componentIds = Component::get(['id'])->pluck(['id'])->toArray();
$attributableIdsToDelete = Attributable::get(['id', 'component_attribute_key'])->filter(function (Attributable $attributable) use($componentIds) {
$attributableComponentId = ComponentAttributeKey::getComponentIdFromString($attributable->component_attribute_key);
$delete = !in_array($attributableComponentId, $componentIds, true);
// debugbar()->debug('Attributeable with id "'.$attributable->id.'" has a component_attribute_key, that refered a component with id "'.$attributableComponentId.'" '.($delete ? ' that does not exist anymore. And thus the attributable is going to be deleted. ': 'that exist. So we leave the attributable.'));
return $delete; //Return the attributable when it has an id that does refer a deleted component
})->map(function (Attributable $attributableToDelete) {
return $attributableToDelete->id;
})
->toArray();
Attributable::destroy($attributableIdsToDelete);
}
/**
* @param ComponentSaveState $componentSaveState
* @param ComponentArea $componentArea
* @param AbstractComponentType $componentType
* @param int $component_id_before_save
*/
public function removeAttributableAttributesFromComponentSaveState(ComponentSaveState $componentSaveState, ComponentArea $componentArea, AbstractComponentType $componentType, int $component_id_before_save)
{
foreach($componentType->getAttributes() as $attribute) {
//Check Autocomplete inputs which "canBeLinked" to other models.
if(is_a($attribute, MultiSelect::class)) {
/** @var MultiSelect $attribute */
if ($attribute->isFilledByProvider() === false) {
continue;
}
$canBeLinkedWith = $attribute->getCanBeLinkedWith();
if ($canBeLinkedWith === '') {
continue;
}
$componentAttributeKeyBeforeSave = ComponentAttributeKey::createInstance(
$component_id_before_save,
$attribute->getsValueFromReference(),
AttributeKey::getAttributeShortClassNameFromString($componentArea->getKey()),
AttributeKey::getValuePartFromString($componentArea->getKey()),
AttributeKey::getTranslationISO2FromString($componentArea->getKey())
);
$attributeData = $componentSaveState->getData();
unset($attributeData[(string) $componentAttributeKeyBeforeSave]);
$componentSaveState->setData($attributeData);
}
}
}
/**
* @param ComponentSaveState $componentSaveState
* @param int $component_id
* @return ComponentSaveState
* @throws \ReflectionException
*/
public function loadAttributablesForComponentAttributes(ComponentSaveState $componentSaveState, int $component_id): ComponentSaveState
{
// //Re-create the component attribute key
$componentSaveStateData = $componentSaveState->getData();
if(count($componentSaveStateData) > 0) {
$firstComponentAttributeKey = array_keys($componentSaveStateData)[0];
$firstComponentAttributeKey = ComponentAttributeKey::createInstanceFromString($firstComponentAttributeKey);
} else {
return $componentSaveState; //We cannot load attributables, because no attributes means no attributables.
}
//Get the components type, then that types attributes.
$componentType = ComponentTypeFactory::make($componentSaveState->getComponentTypeId());
foreach ($componentType->getAttributes() as $attribute) {
/** @var Attribute $attribute */
if(is_a($attribute, MultiSelect::class)) {
//An autocomplete input that was pre-filled and "canBeLinkedWith" other models is an autocomplete input that links attributables.
/** @var MultiSelect $attribute */
if ($attribute->isFilledByProvider() === false) {
continue;
}
$canBeLinkedWith = $attribute->getCanBeLinkedWith();
if ($canBeLinkedWith === '') {
continue;
}
//Create a component attribute key to look up the attributables for this attribute in the database
$componentAttributeKey = ComponentAttributeKey::createInstance(
$component_id,
$attribute->getsValueFromReference(),
ComponentAttributeKey::getAttributeShortClassNameFromString($firstComponentAttributeKey),
ComponentAttributeKey::getValuePartFromString($firstComponentAttributeKey),
ComponentAttributeKey::getTranslationISO2FromString($firstComponentAttributeKey)
);
//Get the attributables for this component's current attribute
$valuesAsArray = Attributable::where([
'component_attribute_key' => $componentAttributeKey,
'attributable_type' => $canBeLinkedWith,
])->get()->map(function(Attributable $attributable) {
return $attributable->attributable_id;
})->toArray();
//Load all components attribute attributables into the component area savestate, separated by comma. Just like the autocomplete delivers.
$attributeData = $componentSaveState->getData();
$attributeData[(string) $componentAttributeKey] = implode(',', $valuesAsArray);
$componentSaveState->setData($attributeData);
}
}
return $componentSaveState;
}
}