File: D:/HostingSpaces/SBogers10/farmfun.komma.pro/app/Komma/Products/Models/Product.php
<?php
namespace App\Komma\Products\Models;
use App\Komma\Documents\DocumentsTrait;
use App\Komma\Documents\Kms\DocumentableInterface;
use App\Komma\Documents\Models\Document;
use App\Komma\Globalization\Languages\Models\Language;
use App\Komma\Kms\Core\AbstractTranslatableModel;
use App\Komma\Kms\Core\Entities\DisplayNameInterface;
use App\Komma\Kms\Core\Entities\DisplayNameTrait;
use App\Komma\Locations\Models\Location;
use App\Komma\Sites\HasSitesInterface;
use App\Komma\Sites\Models\Site;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Str;
/**
* Class Page
*
* @property int site_id
* @property int lft
* @property int rgt
* @property int tree
* @property-read Carbon $date
* @property-read \App\Komma\Globalization\Languages\Models\Language[] $languages
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Komma\Sites\Models\Site[] $sites
* @property-read \App\Komma\Products\Models\ProductTranslation $translation
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Komma\Products\Models\ProductTranslation[] $translations
* @mixin \Eloquent
* @property int $id
* @property int $active
* @property \Carbon\Carbon|null $created_at
* @property \Carbon\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|\App\Komma\Products\Models\Product whereActive($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Komma\Products\Models\Product whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Komma\Products\Models\Product whereDate($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Komma\Products\Models\Product whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|\App\Komma\Products\Models\Product whereUpdatedAt($value)
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Komma\Documents\Models\Document[] $documents
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Komma\Documents\Models\Document[] $images
* @method static \Illuminate\Database\Eloquent\Builder|\App\Komma\Products\Models\Product newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Komma\Products\Models\Product newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\App\Komma\Products\Models\Product query()
*/
final class Product extends AbstractTranslatableModel implements HasSitesInterface, DocumentableInterface, DisplayNameInterface
{
use DocumentsTrait;
use DisplayNameTrait;
use SoftDeletes;
// Maybe in future we want this as a own class
public static $types = [
'activity',
'food',
];
protected $class = self::class;
protected $protected = ['id', 'created_at', 'updated_at'];
/**
* Gets the translation models for this model
*
* @return HasMany that resolves to AbstractTranslationModel instances
*/
public function translations(): HasMany
{
return $this->hasMany(ProductTranslation::class);
}
public function languages(): BelongsToMany
{
return $this->belongsToMany(Language::class, 'product_translations')
->withPivot('slug', 'name', 'description')
->withTimestamps();
}
/**
* Get the site or sites for this model
*
* @return BelongsToMany
*/
public function locations(): BelongsToMany
{
return $this->belongsToMany(Location::class, 'location_products')
->withPivot('max_persons_each_block', 'available_each_block', 'capacity_type');
}
/**
* Get the site or sites for this model
*
* @return BelongsToMany
*/
public function sites(): BelongsToMany
{
return $this->belongsToMany(Site::class, 'site_products');
}
/**
* Get the images through documents belonging to this model
*
* @return \Illuminate\Database\Eloquent\Relations\hasMany
*/
public function overviewImage():MorphOne
{
return $this->morphOne(Document::class, 'documentable')
->where('key', '=', 'Documents-products_index_image');
}
/**
* Get the images through documents belonging to this model
*
* @return \Illuminate\Database\Eloquent\Relations\hasMany
*/
public function quoteImage():MorphOne
{
return $this->morphOne(Document::class, 'documentable')
->where('key', '=', 'Documents-products_quote_image');
}
/**
* Get the images through documents belonging to this model
*
* @return \Illuminate\Database\Eloquent\Relations\hasMany
*/
public function detailImages():MorphMany
{
return $this->morphMany(Document::class, 'documentable')
->where('key', '=', 'Documents-product_detail_images')
->orderBy('sort_order');
}
/**
* Get score attributes
*
* @return object
*/
public function getStats()
{
$attributes = $this->getAttributes();
$stats = Arr::where($attributes, function ($value, $key) {
if (Str::startsWith($key, 'score_') && $value != 0) {
return $value;
}
});
return (object) $stats;
}
/**
* @return bool
*/
public function hasTimeSelection(): bool
{
return ! in_array($this->id, [33, 34]);
}
/**
* Get the label for pricing
* We have defined this here to keep the blades clean.
*
* @param bool $withVat
* @param bool $withCurrency
* @param bool $withSmallTags
* @return string
*/
public function getPriceLabel(bool $withVat = true, bool $withCurrency = true, bool $withSmallTags = true)
{
// Price each person without start up
if (! $this->has_fixed_price && $this->price_start_up === 0) {
if ($withVat) {
if ($withCurrency) {
return config('site.shop_currency').' '.euro_pricing_format($this->price_each_unit);
} else {
return euro_pricing_format($this->price_each_unit);
}
} else {
if ($withCurrency) {
return config('site.shop_currency').' '.euro_pricing_format($this->price_each_unit_excluding_vat);
} else {
return euro_pricing_format($this->price_each_unit_excluding_vat);
}
}
}
// Price is fixed
if ($this->has_fixed_price) {
if ($withVat) {
return euro_pricing_format($this->price_start_up);
} else {
if ($withCurrency) {
return config('site.shop_currency').' '.euro_pricing_format($this->price_start_up_excluding_vat);
} else {
return euro_pricing_format($this->price_start_up_excluding_vat);
}
}
}
// Price for each person and with additional start up price
if (! $this->has_fixed_price && $this->price_start_up !== 0) {
// In the main price (with vat) we only communicate the price each person, on the price line we notify the startup price
if ($withVat) {
$priceLabel = euro_pricing_format($this->price_each_unit);
if ($withSmallTags) {
$priceLabel .= '<small>';
}
$priceLabel .= ' '.__('site/availability.price_each_person');
if ($withSmallTags) {
$priceLabel .= '</small>';
}
return $priceLabel;
} else {
if ($withCurrency) {
return config('site.shop_currency').' '.euro_pricing_format($this->price_each_unit_excluding_vat).' + '.config('site.shop_currency').' '.euro_pricing_format($this->price_start_up);
} else {
return euro_pricing_format($this->price_each_unit_excluding_vat).' + '.config('site.shop_currency').' '.euro_pricing_format($this->price_start_up);
}
}
}
throw new \UnexpectedValueException(self::class.': We have not defined the pricing label for the given situation "'.$this->code_name.'"');
}
/**
* Get the label for the line below the price on the poster.
* We have defined this here to keep the blades clean.
*
* @return array|string|null
*/
public function getPosterLabel()
{
// Price each person without start up
if (! $this->has_fixed_price && $this->price_start_up === 0) {
return __('site/availability.price_each_person');
}
// Price is fixed
if ($this->has_fixed_price) {
$customLabelKey = 'site/availability.custom_start_up.'.$this->code_name;
$customLabel = __($customLabelKey);
// If the translation is the same, it isn't found.
if ($customLabelKey === $customLabel) {
debug(self::class.': Custom key not found for "'.$this->code_name.'"');
return __('site/availability.start_up');
} else {
return $customLabel;
}
}
// Price for each person and with additional start up price
if (! $this->has_fixed_price && $this->price_start_up !== 0) {
return '+ '.config('site.shop_currency').' '.euro_pricing_format($this->price_start_up, ',', '', true).' '.__('site/availability.start_up');
}
throw new \UnexpectedValueException(self::class.': We have not defined the price line for the given situation "'.$this->code_name.'"');
}
/**
* Get the label for the line below the price on the shopping cart
* We have defined this here to keep the blades clean.
*
* @param bool $withVat
* @param bool $formatted
* @return array|string|null
*/
public function getCartLabel(bool $withVat = true, bool $formatted = true)
{
// Price each person without start up
if (! $this->has_fixed_price && $this->price_start_up === 0) {
return config('site.shop_currency').' '.$this->getPriceLabel($withVat, $formatted).' '.__('site/availability.price_each_person');
}
// Price is fixed
if ($this->has_fixed_price) {
// Is the same as the poster line
return $this->getPosterLabel();
}
// Price for each person and with additional start up price
if (! $this->has_fixed_price && $this->price_start_up !== 0) {
$cartLine = config('site.shop_currency').' '.$this->getPriceLabel($withVat, $formatted, false);
$cartLine .= ' <br/>+ '.config('site.shop_currency').' '.euro_pricing_format($this->price_start_up).' '.__('site/availability.start_up_small');
return $cartLine;
}
}
/**
* Get the label for duration
*
* @return string
*/
public function getDurationLabel()
{
// Makes sure that we don't have zero's add the end.
$duration = floatval($this->duration);
// Remove zero if not needed behind the digits
$duration = str_replace('.0', '', $duration);
// Replace . to ,
$duration = str_replace('.', ',', $duration);
return $duration.' '.__('site/availability.hour');
}
/**
* Look up if this product has been marked
* as available for this location.
*
* @param Location $location
* @return bool
*/
public function availableForLocation(Location $location): bool
{
$this->loadMissing('locations');
$productCartLocation = $this->locations->first(fn ($l) => $l->id === $location->id);
if ($productCartLocation) {
return true;
}
return false;
}
}