File: D:/HostingSpaces/SBogers10/douven.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($paginate = null)
{
if (!empty($paginate)) {
$allItems = CatalogIndexModel::paginate($paginate);
} else {
$allItems = CatalogIndexModel::all();
}
foreach ($allItems as $item) {
$type = $item->catalogable_type;
$id = $item->catalogable_id;
$item->item = $type::where('id', $id)->with(['translation', 'images', 'categories' => function($query) {
$query->with('translations');
}])->orderBy('created_at', 'desc')->first();
}
return $allItems;
}
public function getItemsByCategoryID($categoryID, $paginate = null, $currentPage)
{
$allItems = CatalogIndexModel::all()->sortBy('updated_at')->reverse();
foreach ($allItems as $item) {
$type = $item->catalogable_type;
$id = $item->catalogable_id;
$item->item = $type::where('id', $id)->with(['translation', 'images', 'categories' => function($query) {
$query->with('translations');
}])->first();
}
$allItems = $allItems->filter(function (CatalogIndexModel $catalogIndexModel) use ($categoryID) {
return count($catalogIndexModel->item->categories) > 0 && $catalogIndexModel->item->categories[0]->id == $categoryID;
});
if (!empty($paginate)) {
//$allItems->paginate($paginate);
$page = $currentPage;
$perPage = $paginate;
$allItems = new \Illuminate\Pagination\LengthAwarePaginator(
$allItems->forPage($page, $perPage),
$allItems->count(),
$perPage,
$page
);
}
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());
}
}