20-02-2026

This commit is contained in:
Kevin Adametz 2026-02-20 17:57:50 +01:00
parent 854ce02bf6
commit 4d6b4930b2
128 changed files with 18247 additions and 2093 deletions

246
app/Models/Product.php Normal file
View file

@ -0,0 +1,246 @@
<?php
namespace App\Models;
use App\Enums\PriceType;
use App\Enums\ProductStatus;
use App\Enums\ProductType;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
class Product extends Model
{
use HasFactory;
protected $fillable = [
'partner_id',
'partner_product_number',
'b2in_article_number',
'brand_id',
'collection_id',
'hub_id',
'name',
'slug',
'product_type',
'status',
'price_type',
'price_display_text',
'description_short',
'description_long',
'care_instructions',
'width_cm',
'height_cm',
'depth_cm',
'dimensions_specific',
'assembly_status',
'meta_title',
'meta_description',
'is_curated',
'curated_at',
'curated_by',
'curation_notes',
'is_available',
'country_of_origin',
'main_material',
'surface_material',
'cover_material',
'color_finish',
'certificates',
'assembly_time_min',
'load_capacity_kg',
'delivery_type',
'assembly_service',
'service_radius_km',
'warranty_months',
'production_time_days',
'productIsAvailable',
'visible_from',
'visible_until',
'co2_footprint_kg',
'recycling_percentage',
'is_regional_production',
'storage_volume_liters',
'assembly_effort_score',
'design_score',
];
/**
* @return array<string, string>
*/
protected function casts(): array
{
return [
'product_type' => ProductType::class,
'status' => ProductStatus::class,
'price_type' => PriceType::class,
'dimensions_specific' => 'array',
'is_curated' => 'boolean',
'curated_at' => 'datetime',
'is_available' => 'boolean',
'productIsAvailable' => 'boolean',
'certificates' => 'array',
'assembly_service' => 'boolean',
'is_regional_production' => 'boolean',
'visible_from' => 'date',
'visible_until' => 'date',
'co2_footprint_kg' => 'decimal:2',
];
}
/**
* Produkt gehört zu einem Partner (Händler oder Hersteller).
*/
public function partner(): BelongsTo
{
return $this->belongsTo(Partner::class);
}
/**
* Produkt gehört zu einer Marke.
*/
public function brand(): BelongsTo
{
return $this->belongsTo(Brand::class);
}
/**
* Produkt gehört zu einer Kollektion.
*/
public function collection(): BelongsTo
{
return $this->belongsTo(Collection::class);
}
/**
* Produkt gehört zu einem Hub (regionale Zuordnung).
*/
public function hub(): BelongsTo
{
return $this->belongsTo(Hub::class);
}
/**
* User, der das Produkt kuratiert/freigegeben hat.
*/
public function curator(): BelongsTo
{
return $this->belongsTo(User::class, 'curated_by');
}
/**
* Produkt kann mehreren Kategorien zugeordnet sein.
*/
public function categories(): BelongsToMany
{
return $this->belongsToMany(Category::class);
}
/**
* Produkt kann mehrere Tags haben.
*/
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class);
}
/**
* Produkt hat mehrere Varianten (Farben, Größen, etc.).
*/
public function variants(): HasMany
{
return $this->hasMany(ProductVariant::class);
}
/**
* Polymorphe Beziehung zu Media (Bilder, Videos, PDFs).
*/
public function media(): MorphMany
{
return $this->morphMany(Media::class, 'model');
}
/**
* Holzherkunft-Einträge für EUDR-Compliance.
*/
public function woodOrigins(): HasMany
{
return $this->hasMany(ProductWoodOrigin::class);
}
/**
* Änderungshistorie.
*/
public function activities(): HasMany
{
return $this->hasMany(ProductActivity::class);
}
/**
* Verwandte Produkte.
*/
public function relatedProducts(): BelongsToMany
{
return $this->belongsToMany(self::class, 'related_products', 'product_id', 'related_product_id');
}
/**
* Scope: Nur aktive Produkte.
*/
public function scopeActive(Builder $query): void
{
$query->where('status', ProductStatus::Active);
}
/**
* Scope: Nur kuratierte/freigegebene Produkte.
*/
public function scopeCurated(Builder $query): void
{
$query->where('is_curated', true);
}
/**
* Scope: Nur Produkte, die auf Freigabe warten.
*/
public function scopePending(Builder $query): void
{
$query->where('status', ProductStatus::Pending);
}
/**
* Scope: Nur Local Stock Produkte (Säule A).
*/
public function scopeLocalStock(Builder $query): void
{
$query->where('product_type', ProductType::LocalStock);
}
/**
* Scope: Nur Smart Order Produkte (Säule B).
*/
public function scopeSmartOrder(Builder $query): void
{
$query->where('product_type', ProductType::SmartOrder);
}
/**
* Scope: Produkte in einem bestimmten Hub.
*/
public function scopeInHub(Builder $query, int $hubId): void
{
$query->where('hub_id', $hubId);
}
/**
* Scope: Verfügbare Produkte (aktiv, kuratiert, verfügbar).
*/
public function scopeAvailable(Builder $query): void
{
$query->active()->curated()->where('is_available', true);
}
}