HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
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;
    }
}