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/kemi.komma.pro/app/KommaApp/Shop/Catalog/Kms/CatalogService.php
<?php
namespace App\KommaApp\Shop\Catalog\Kms;


use App\KommaApp\Kms\Core\Sections\SectionService;
use App\KommaApp\Kms\Core\Sections\SectionServiceInterface;
use App\KommaApp\Shop\Products\Product\ProductServiceInterface;
use App\KommaApp\Shop\Products\ProductComposite\ProductCompositeServiceInterface;
use App\KommaApp\Shop\Products\ProductGroup\ProductGroupServiceInterface;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Schema;

/**
 * Builds the product catalog for the frontend
 *
 * Class CatalogService
 * @package App\KommaApp\Shop\Catalog
 */
class CatalogService extends SectionService implements CatalogServiceInterface
{
    /**
     * @var string[] Attributes that can be indexed / sorted. Used for creating the migration. Make sure you also reflect these variables in the CatalogIndexModel
     */
    private $attributes;

    /**
     * @var Model[] $catalogable items
     */
    private $catalogableServicesInterfaces = [];

    /**
     * CatalogService constructor
     */
    public function __construct()
    {
        $this->forModelName = CatalogIndexModel::class;

        $this->catalogableServicesInterfaces[] = ProductServiceInterface::class;
        $this->catalogableServicesInterfaces[] = ProductGroupServiceInterface::class;
        $this->catalogableServicesInterfaces[] = ProductCompositeServiceInterface::class;

        $this->attributes = [
            'title' => 'string',
            'price' => 'integer'
        ];

        parent::__construct();
    }

    public function getAllItems() {
        $allItems = CatalogIndexModel::all();

        foreach ($allItems as $item) {
                $type = $item->catalogable_type;
                $id = $item->catalogable_id;
                $item->item = $type::with('translation')->where('id', $id)->first();
            }

        return $allItems;
    }

    /**
     * Generate the model index that can be searched trough, paginated, sorted etc.
     * It will use the attributes keys as attribute names on the models retrieved from all catalogableServiceInterface implementations.
     * These attribute keys will be set with their values on the CatalogIndexModel that wil be saved to the database.
     * If the catalog model does not have one of the attributes that are specified in the attributes value it will
     */
    public function createIndex()
    {
        $this->doHouseKeeping();

        \Log::debug('CatalogService indexing...');
        $this->validateCatalogIndexModelDefinition();

        foreach($this->catalogableServicesInterfaces as $catalogableServiceInterface)
        {
            /** @var SectionServiceInterface $service */
            $service = \App::make($catalogableServiceInterface);

            $modelCollection = $service->models()->get();
            if(!$modelCollection) continue;

            $modelCollection->each(function($model) use ($service) {
//                $catalogIndexModel = new $this->forModelName;
                $catalogIndexModel = $this->forModelName::firstOrNew([
                    'catalogable_type' => $service->getForModelName(),
                    'catalogable_id' => $model->id
                ]);

                /** @var $model Model */
                foreach($this->attributes as $attribute => $type)
                {
                    if($this->modelHasAttribute($model, $attribute)) {
                        $catalogIndexModel->{$attribute} = $model->$attribute;
                        $catalogIndexModel->catalogable_id = $model->id;
                        $catalogIndexModel->catalogable_type = $service->getForModelName();
                    }
                }

                $catalogIndexModel->save();
            });
        }

        \Log::debug('CatalogService indexing done!');
    }

    /**
     * Clear the catalog.
     */
    public function clearIndex()
    {
        \Log::debug('CatalogService clearing index...');
        $this->forModelName::truncate();
        \Log::debug('CatalogService cleared index!');
    }

    /**
     * Clean up old catalog index items that reference non existing models.
     */
    public function doHouseKeeping()
    {
        \Log::debug('CatalogService doing some housekeeping...');

        $debugCounter = 0;

        /** @var CatalogIndexModel[] $indexModels */
        $indexModels = $this->forModelName::get(['id', 'catalogable_type', 'catalogable_id']);
        $indexModels->each(function ($indexModel) use(&$debugCounter) {
            /** @var CatalogIndexModel $indexModel */
            //If the catalogable_type class does not exist we need to delete it.
            if(!class_exists($indexModel->catalogable_type)) {
                $indexModel->delete();
                return; //skip to the next "each" iteration
            }

//            if($debugCounter == 1) dd($indexModel->catalogable_type::where('id', '=', $indexModel->catalogable_id)->get()); //debugging helper
            //Check if there is a catalogable_type class with the id specified. If not delete the indexModel
            if($indexModel->catalogable_type::where('id', '=', $indexModel->catalogable_id)->get()->count() == 0) {
                $indexModel->delete();
            }

            $debugCounter++;
        });

        \Log::debug('CatalogService done housekeeping!');
    }

    /**
     * Checks if all attributes defined in this service are also attributes on the CatalogIndexModel
     */
    private function validateCatalogIndexModelDefinition()
    {
        $testModel = new $this->forModelName;
        foreach($this->attributes as $attribute => $type)
            if(!$this->modelHasAttribute($testModel, $attribute)) throw new \RuntimeException('The "' . $this->forModelName . '" class did not have the "' . $attribute . '" attribute. Make sure it exists in the model and in the schema table ('.$testModel->getTable().') it belongs to');
    }

    /**
     * Checks is an eloquent model has the specified attribute or attributes and return true if it has the attribute(s) or false if not
     *
     * @param Model $model
     * @param $attributeName
     * @return bool
     */
    private function modelHasAttribute(Model $model, $attributeName):bool
    {
        if(!is_string($attributeName) && !is_array($attributeName)) throw new \RuntimeException('The specified columnName isn\'t an array or a string');
        if(!is_array($attributeName)) $attributeName = [$attributeName];

        $hasAttribute = true;
        foreach($attributeName as $attribute)
        {
            if(!\Schema::hasColumn($model->getTable(), $attribute)) {
                $hasAttribute = false;
            }
        }

        return $hasAttribute;
    }

    /**
     * Create the catalog table. You can use this in your migrations
     */
    public function migrate()
    {
        Schema::create((new $this->forModelName)->getTable(), function (Blueprint $table) {
            $table->increments('id');
            $table->string('catalogable_id');
            $table->string('catalogable_type');

            foreach($this->attributes as $attribute => $type)
            {
                $table->{$type}($attribute);
            }

            $table->timestamps();
        });
    }

    /**
     * Remove the catalog table. You can use this in your migrations
     */
    public function rollback()
    {
        Schema::dropIfExists((new $this->forModelName)->getTable());
    }
}