This commit is contained in:
2025-09-25 03:03:31 +05:00
commit ae480cf2f6
2768 changed files with 1485826 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
<?php
namespace App\Models\Auth;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Verification extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'username',
'code',
];
}

View File

@@ -0,0 +1,47 @@
<?php
namespace App\Models\CMS\Forms;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ContactUS extends Model
{
use HasFactory;
/**
* The table associated with the model.
*/
protected $table = 'contact_us';
/**
* The attributes that are mass assignable.
*
* title - string
* phone - string
* content - text
* type - string | in: website,admin,mobile_admin,mobile_app
* user_id - int
*
* @var array<string>
*/
protected $fillable = [
'title',
'phone',
'content',
'type',
'user_id',
];
/**
* User
*
* User that submitted form
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Models\CMS\Marketing;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Newsletter extends Model
{
use HasFactory;
/**
* The table associated with the model.
*/
protected $table = 'newsletters';
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'subject',
'content',
'sent',
];
}

View File

@@ -0,0 +1,23 @@
<?php
namespace App\Models\CMS\Marketing;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class NewsletterUser extends Model
{
use HasFactory;
/**
* The table associated with the model.
*/
protected $table = 'newsletter_users';
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'email',
];
}

View File

@@ -0,0 +1,246 @@
<?php
namespace App\Models\CMS\Media;
use App\Models\Concerns\HasSchemalessAttributes;
use App\Models\Ecommerce\Product\Brand\Brand;
use App\Models\Ecommerce\Product\Category\Category;
use App\Models\Ecommerce\Product\Collection\Collection;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\EloquentSortable\SortableTrait;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Translatable\HasTranslations;
class Banner extends Model implements HasMedia
{
use HasFactory;
use HasSchemalessAttributes;
use HasTranslations;
use InteractsWithMedia;
use SortableTrait;
/**
* Table
*/
protected $table = 'banners';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'id',
'app',
'title',
'description',
'place',
'link',
'is_visible',
'resource_id',
'resource_type',
'sort_order',
'options',
];
/**
* Translatable attributes
*
* @var array<string>
*/
public $translatable = ['title', 'description'];
/**
* Sortable attributes
*
* @var array<string, mixed>
*/
public $sortable = [
'order_column_name' => 'sort_order',
'sort_when_creating' => true,
];
/**
* Banner Position: First
*/
public const PLACE_FIRST = 'first';
/**
* Banner Position: Market
*/
public const MARKET = 'market';
/**
* Banner Position: Store
*/
public const STORE = 'store';
/**
* Banner Position: Homepage Grids
*/
public const HOMEPAGE_2_GRIDS = 'homepage_2_grids';
/**
* Banner Position: Homepage categories
*/
public const HOMEPAGE_CATEGORIES = 'homepage_categories';
/**
* Media collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('main')
->singleFile();
}
/**
* Media conversations
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb200x200')
->fit(Manipulations::FIT_CONTAIN, 200, 200);
$this->addMediaConversion('thumb400x400')
->fit(Manipulations::FIT_CONTAIN, 400, 400);
$this->addMediaConversion('thumb350x350')
->fit(Manipulations::FIT_CONTAIN, 350, 350);
$this->addMediaConversion('thumb720x720')
->fit(Manipulations::FIT_CONTAIN, 720, 720);
$this->addMediaConversion('thumb800x800')
->fit(Manipulations::FIT_CONTAIN, 800, 800);
$this->addMediaConversion('thumb1200x1200')
->fit(Manipulations::FIT_CONTAIN, 1200, 1200);
$this->addMediaConversion('thumb288x431')
->fit(Manipulations::FIT_CONTAIN, 288, 431);
$this->addMediaConversion('thumb270x350')
->fit(Manipulations::FIT_CONTAIN, 270, 350);
}
/**
* Category
*/
public function category(): BelongsTo
{
return $this->belongsTo(Category::class, 'resource_id');
}
/**
* Collection
*/
public function collection(): BelongsTo
{
return $this->belongsTo(Collection::class, 'resource_id');
}
/**
* Product
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class, 'resource_id');
}
/**
* Brand
*/
public function brand(): BelongsTo
{
return $this->belongsTo(Brand::class, 'resource_id');
}
/**
* Thumbnail URL
*/
public function thumbnail(string $resolution = '50x50'): string
{
return $this->getFirstMediaUrl('main', 'thumb'.$resolution);
}
/**
* Original image
*/
public function image(): string
{
return $this->getFirstMediaUrl('main');
}
/**
* Scope to filter
*
* @param Builder $query
* @param string $attribute
* @param string $value
* @return Builder $query
*/
public function scopeFilter($query, $attribute, $value): Builder
{
return $value ? $query->where($attribute, $value) : $query;
}
/**
* Text color
*/
public function textColor(): ?string
{
return $this->options->get('color');
}
/**
* Places for banner
*
* @return string
*/
public static function places(): array
{
return [
'first' => __('First banner'),
'market' => 'Market Baş sahypa',
'store' => 'Store Baş sahypa',
'homepage_2_grid' => __('Homepage 2 grid'),
'homepage_categories' => __('Homepage categories'),
];
}
/**
* Attachable resourcec to banner
*/
public static function attachableResources(): array
{
return [
'brand' => __('Brand'),
'category' => __('Category'),
'collection' => __('Collection'),
];
}
/**
* Related resources
*/
public function relatedResources()
{
if ($this->place === 'homepage_categories') {
if ($this->resource_type === 'category') {
$this->load('category');
return $this->category->children()->enabled()->ordered()->get();
}
}
return [];
}
}

View File

@@ -0,0 +1,181 @@
<?php
namespace App\Models\CMS\Media;
use App\Models\Concerns\HasSchemalessAttributes;
use App\Models\Ecommerce\Product\Brand\Brand;
use App\Models\Ecommerce\Product\Category\Category;
use App\Models\Ecommerce\Product\Collection\Collection;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\EloquentSortable\SortableTrait;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Translatable\HasTranslations;
class Carousel extends Model implements HasMedia
{
use HasFactory;
use HasSchemalessAttributes;
use HasTranslations;
use InteractsWithMedia;
use SortableTrait;
/**
* Table
*
* @var string
*/
protected $table = 'carousels';
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'id',
'app',
'title',
'description',
'place',
'link',
'is_visible',
'resource_id',
'resource_type',
'sort_order',
'options',
];
/**
* Transtable fields
*
* @var array<string>
*/
public array $translatable = ['title', 'description'];
/**
* Sortable attributes
*
* @var array<string, mixed>
*/
public $sortable = [
'order_column_name' => 'sort_order',
'sort_when_creating' => true,
];
/**
* Category
*/
public function category(): BelongsTo
{
return $this->belongsTo(Category::class, 'resource_id');
}
/**
* Collection
*/
public function collection(): BelongsTo
{
return $this->belongsTo(Collection::class, 'resource_id');
}
/**
* Brand
*/
public function brand(): BelongsTo
{
return $this->belongsTo(Brand::class, 'resource_id');
}
/**
* Media collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('main')
->singleFile();
}
/**
* Media conversations
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb50x50')
->width(50)
->height(50);
$this->addMediaConversion('thumb350x350')
->width(350)
->height(350);
$this->addMediaConversion('thumb650x650')
->width(650)
->height(650);
}
/**
* Thumbnail
*/
public function thumbnail(string $resolution = '50x50'): string
{
return $this->getFirstMediaUrl('main', 'thumb'.$resolution);
}
/**
* Scope to filter
*
* @param mixed $query
* @param string $attribute
* @param string $value
* @return $query
*/
public function scopeFilter($query, $attribute, $value): Builder
{
return $value ? $query->where($attribute, $value) : $query;
}
/**
* Main image
*/
public function image(): string
{
return $this->getFirstMediaUrl('main');
}
/**
* Text color
*/
public function textColor(): string
{
return $this->options->get('color') ?? '';
}
/**
* Places for banner
*
* @return string
*/
public static function places(): array
{
return [
'homepage' => __('Homepage'),
];
}
/**
* Attachable resourcec to banner
*/
public static function attachableResources(): array
{
return [
'brand' => __('Brand'),
'category' => __('Category'),
'collection' => __('Collection'),
];
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace App\Models\CMS\Media;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Translatable\HasTranslations;
class Gallery extends Model implements HasMedia
{
use HasFactory;
use HasTranslations;
use InteractsWithMedia;
/**
* Table
*/
protected $table = 'galleries';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'title',
'description',
'is_visible',
];
/**
* @var array<string>
*/
public $translatable = ['title', 'description'];
/**
* Media collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('main')
->singleFile();
}
/**
* Media conversations
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb')
->width(470)
->height(345)
->fit(Manipulations::FIT_CONTAIN, 470, 345);
}
/**
* Thumbnail url
*/
public function thumbnail(): string
{
return $this->getFirstMediaUrl('main', 'thumb');
}
/**
* Scope for visable galleries
*
* @param Builder $query
*/
public function scopeEnabled($query): void
{
$query->where('is_visible', true);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Models\Common;
use App\Models\Ecommerce\Product\Product\Product;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Comment extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'comment',
'user_id',
'active',
];
/**
* User
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Comment's attached product
*/
public function product(): BelongsToMany
{
return $this->belongsToMany(Product::class);
}
}

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Models\Concerns;
use App\Models\Ecommerce\Channel\Channel;
use Illuminate\Database\Eloquent\Relations\MorphMany;
trait HasEcommerceChannels
{
/**
* User ecommerce channels
*/
public function channels(): MorphMany
{
return $this->morphMany(Channel::class, 'channelables');
}
/**
* Get the user's most recent image.
*/
public function channel(): ?Channel
{
return $this->relationLoaded('channels')
? $this->channels->first()
: $this->channels()->first();
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace App\Models\Concerns;
use Illuminate\Database\Eloquent\Builder;
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
trait HasSchemalessAttributes
{
/**
* Add Schemaless Attributes to casts
*/
public function initializeHasSchemalessAttributes(): void
{
$this->casts['options'] = SchemalessAttributes::class;
}
/**
* Scope for Extra attributes
*/
public function scopeWithExtraAttributes(): Builder
{
return $this->options->modelScope();
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Models\Concerns;
trait InteractsWithNova
{
/**
* Check if user can access nova
*/
public function canAccessNova(): bool
{
if ($this->isMe()) {
return true;
}
if ($this->hasRole(['admin', 'manager'])) {
return true;
}
if ($this->hasRole('vendor') && $this->channel()->id) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Models\Concerns;
trait InteractsWithRoles
{
/**
* Check if user is Me :)
*/
public function isMe(): bool
{
return in_array($this->email, [
'nurmuhammet@mail.com',
]);
}
/**
* Check if user is admin
*/
public function isAdmin(): bool
{
if ($this->isMe()) {
return true;
}
return $this->hasRole('admin');
}
/**
* Check if user is manager
*/
public function isManager(): bool
{
return $this->hasRole('manager');
}
/**
* Check if user is entrepreneur
*/
public function isEntrepreneur(): bool
{
return $this->hasRole('vendor');
}
/**
* Check if user is vendor
*/
public function isVendor(): bool
{
return $this->hasRole('vendor');
}
}

View File

@@ -0,0 +1,218 @@
<?php
namespace App\Models\Ecommerce\Channel;
use App\Events\Ecommerce\Channel\ChannelActiveStateChanged;
use App\Models\Ecommerce\Product\Inventory\Inventory;
use App\Models\Ecommerce\Product\Product\Product;
use App\Models\User;
use App\Repositories\Ecommerce\Channel\ChannelRepository;
use App\Repositories\System\Cache\CacheRepository;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Laravel\Nova\Nova;
use Spatie\EloquentSortable\Sortable;
use Spatie\EloquentSortable\SortableTrait;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
class Channel extends Model implements HasMedia, Sortable
{
use HasFactory;
use HasSlug;
use InteractsWithMedia;
use SortableTrait;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'id',
'name',
'slug',
'description',
'timezone',
'url',
'sort_order',
'is_default',
'is_visible',
'channelables_type',
'channelables_id',
];
/**
* Sortable attributes
*
* @var array<string, mixed>
*/
public $sortable = [
'order_column_name' => 'sort_order',
'sort_when_creating' => true,
];
/**
* The "booted" method of the model.
*/
protected static function booted(): void
{
static::created(function (Channel $channel) {
CacheRepository::forget(ChannelRepository::CHANNEL_CACHE_NAME_FOR_NOVA);
});
static::updated(function (Channel $channel) {
if ($channel->wasChanged('is_visible')) {
ChannelActiveStateChanged::dispatch($channel);
CacheRepository::forget(ChannelRepository::CHANNEL_CACHE_NAME_FOR_NOVA);
}
});
static::deleted(function (Channel $channel) {
CacheRepository::forget(ChannelRepository::CHANNEL_CACHE_NAME_FOR_NOVA);
});
}
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug');
}
/**
* Media collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('uploads')
->singleFile()
->useFallbackUrl(
sprintf('%s/logo-space.png', config('app.url'))
);
}
/**
* Media conversations
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb150x150')
->fit(Manipulations::FIT_CONTAIN, 150, 150);
$this->addMediaConversion('thumb200x200')
->fit(Manipulations::FIT_CONTAIN, 200, 200);
$this->addMediaConversion('thumb400x400')
->fit(Manipulations::FIT_CONTAIN, 400, 400);
$this->addMediaConversion('thumb720x720')
->fit(Manipulations::FIT_CONTAIN, 720, 720);
$this->addMediaConversion('thumb800x800')
->fit(Manipulations::FIT_CONTAIN, 800, 800);
$this->addMediaConversion('thumb1200x1200')
->fit(Manipulations::FIT_CONTAIN, 1200, 1200);
$this->addMediaConversion('thumb288x431')
->fit(Manipulations::FIT_CONTAIN, 288, 431);
$this->addMediaConversion('thumb270x350')
->fit(Manipulations::FIT_CONTAIN, 270, 350);
}
/**
* Channel thumbnail
*/
public function thumbnail(string $size = '200x200'): string
{
return $this->getFirstMediaUrl('uploads', 'thumb'.$size);
}
/**
* Get the parent channelables model.
*/
public function channelables(): MorphTo
{
return $this->morphTo();
}
/**
* User
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'channelables_id');
}
/**
* Default tmpost inventory
*/
public static function tmpostDefault(): static
{
return static::where('slug', 'tmpost')->first();
}
/**
* Channels inventories
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
// public function inventories(): HasMany
// {
// return $this->hasMany(Inventory::class);
// }
/**
* Products
*/
public function products(): MorphToMany
{
return $this->morphToMany(Product::class, 'productable', 'product_has_relations');
}
/**
* Show products link
*/
public function productsPage(): string
{
return route('web.entrepreneurs.show', ['entrepreneur' => $this->slug]);
}
/**
* Image
*/
public function image(): string
{
return $this->getFirstMediaUrl('main');
}
/**
* Channels inventories
*/
public function inventories(): HasMany
{
return $this->hasMany(Inventory::class);
}
/**
* Nova detail page
*/
public function novaDetailPage(): string
{
return Nova::url('/resources/channels/'.$this->id);
}
}

View File

@@ -0,0 +1,86 @@
<?php
namespace App\Models\Ecommerce\Payouts;
use App\Models\Ecommerce\Channel\Channel;
use App\Models\Ecommerce\Product\Order\OrderItem;
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\Support\Facades\DB;
class Payout extends Model
{
use HasFactory;
/**
* Protected fields
*
* @var array
*/
protected $guarded = [];
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'channel_id',
'start_date',
'end_date',
'total_sum',
'postshop_total',
'entrepreneur_total',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'start_date' => 'date',
'end_date' => 'date',
];
/**
* Related channel
*/
public function channel(): BelongsTo
{
return $this->belongsTo(Channel::class);
}
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::updated(function ($payout) {
$order_item_ids = [];
foreach ($payout->order_items_ids as $key => $value) {
if (! $value) {
$order_item_ids[] = $key;
}
}
DB::table('payout_items')
->where('payout_id', $payout->id)
->whereIntegerInRaw('order_item_id', $order_item_ids)
->delete();
});
}
/**
* Order items
*/
public function orderItems(): BelongsToMany
{
return $this->belongsToMany(OrderItem::class, 'payout_items');
}
}

View File

@@ -0,0 +1,39 @@
<?php
namespace App\Models\Ecommerce\Payouts;
use App\Models\Ecommerce\Channel\Channel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class PayoutItem extends Model
{
use HasFactory;
/**
* Protected fields
*
* @var array
*/
protected $guarded = [];
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'payout_id',
'channel_id',
'order_item_id',
];
/**
* Related channel
*/
public function channel(): BelongsTo
{
return $this->belongsTo(Channel::class);
}
}

View File

@@ -0,0 +1,169 @@
<?php
namespace App\Models\Ecommerce\Product\Brand;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\EloquentSortable\Sortable;
use Spatie\EloquentSortable\SortableTrait;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
use Spatie\Translatable\HasTranslations;
class Brand extends Model implements HasMedia, Sortable
{
use HasFactory;
use HasSlug;
use HasTranslations;
use InteractsWithMedia;
use SortableTrait;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'name',
'slug',
'type',
'website',
'description',
'seo_title',
'seo_description',
'is_visible',
'sort_order',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['description'];
/**
* Sortable attributes
*
* @var array<string, mixed>
*/
public $sortable = [
'order_column_name' => 'sort_order',
'sort_when_creating' => true,
];
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug');
}
/**
* Register media collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('uploads')
->singleFile()
->acceptsMimeTypes(['image/jpg', 'image/jpeg', 'image/png'])
->useFallbackUrl(
sprintf('%s/logo-space.png', config('app.url'))
);
}
/**
* Register Media Conversions
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb200x200')
->fit(Manipulations::FIT_CONTAIN, 200, 200);
$this->addMediaConversion('thumb400x400')
->fit(Manipulations::FIT_CONTAIN, 400, 400);
$this->addMediaConversion('thumb720x720')
->fit(Manipulations::FIT_CONTAIN, 720, 720);
$this->addMediaConversion('thumb800x800')
->fit(Manipulations::FIT_CONTAIN, 800, 800);
$this->addMediaConversion('thumb1200x1200')
->fit(Manipulations::FIT_CONTAIN, 1200, 1200);
$this->addMediaConversion('thumb288x431')
->fit(Manipulations::FIT_CONTAIN, 288, 431);
$this->addMediaConversion('thumb270x350')
->fit(Manipulations::FIT_CONTAIN, 270, 350);
}
/**
* Brand thumbnail
*/
public function thumbnail(string $size = '200x200'): string
{
return $this->getFirstMediaUrl('uploads', 'thumb'.$size);
}
/**
* Original image
*/
public function image(): string
{
return $this->getFirstMediaUrl('uploads');
}
/**
* Brand Products
*/
public function products(): HasMany
{
return $this->hasMany(Product::class);
}
/**
* Scope for visable models
*/
public function scopeEnabled(Builder $query): void
{
$query->where('is_visible', true);
}
/**
* Brand Types
*/
public static function types(): array
{
return [
'market',
];
}
/**
* Check if brand belongs to market
*
* @return bool
*/
public function isMarketCategory(): string
{
return $this->type === 'market';
}
/**
* Brand's Products web page
*/
public function productsPage(): string
{
return route('web.brands.products', ['brand' => $this->slug]);
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace App\Models\Ecommerce\Product\Cart;
use App\Models\Ecommerce\Product\Product\Product;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class CartItem extends Model
{
use HasFactory;
/**
* The table associated with the model.
*/
protected $table = 'cart_items';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'user_id',
'product_id',
'product_quantity',
];
/**
* Cart Item user
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Cart Item product
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
}

View File

@@ -0,0 +1,205 @@
<?php
namespace App\Models\Ecommerce\Product\Category;
use App\Events\Ecommerce\Product\Category\CategoryTaxChanged;
use App\Models\Concerns\HasSchemalessAttributes;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Spatie\EloquentSortable\Sortable;
use Spatie\EloquentSortable\SortableTrait;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
use Spatie\Translatable\HasTranslations;
use Staudenmeir\EloquentEagerLimitXLaravelAdjacencyList\Eloquent\HasEagerLimitAndRecursiveRelationships;
class Category extends Model implements HasMedia, Sortable
{
use HasEagerLimitAndRecursiveRelationships;
use HasFactory;
use HasSchemalessAttributes;
use HasSlug;
use HasTranslations;
use InteractsWithMedia;
use SortableTrait;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'parent_id',
'name',
'slug',
'type',
'description',
'seo_title',
'seo_description',
'is_visible',
'tax_percentage',
'sort_order',
'options',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['name', 'description'];
/**
* Sortable attributes
*
* @var array<string, mixed>
*/
public $sortable = [
'order_column_name' => 'sort_order',
'sort_when_creating' => true,
];
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug');
}
/**
* Media Collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('uploads')
->singleFile()
->acceptsMimeTypes(['image/jpg', 'image/jpeg', 'image/png'])
->useFallbackUrl(
sprintf('%s/logo-space.png', config('app.url'))
);
}
/**
* Register Media Conversions
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb200x200')
->fit(Manipulations::FIT_CONTAIN, 200, 200);
$this->addMediaConversion('thumb400x400')
->fit(Manipulations::FIT_CONTAIN, 400, 400);
$this->addMediaConversion('thumb720x720')
->fit(Manipulations::FIT_CONTAIN, 720, 720);
$this->addMediaConversion('thumb800x800')
->fit(Manipulations::FIT_CONTAIN, 800, 800);
$this->addMediaConversion('thumb1200x1200')
->fit(Manipulations::FIT_CONTAIN, 1200, 1200);
$this->addMediaConversion('thumb288x431')
->fit(Manipulations::FIT_CONTAIN, 288, 431);
$this->addMediaConversion('thumb270x350')
->fit(Manipulations::FIT_CONTAIN, 270, 350);
$this->addMediaConversion('thumb657x230')
->fit(Manipulations::FIT_CROP, 657, 230);
}
/**
* Brand thumbnail
*/
public function thumbnail(string $size = '200x200'): string
{
return $this->getFirstMediaUrl('uploads', 'thumb'.$size);
}
/**
* The "booted" method of the model.
*
* @return void
*/
protected static function booted()
{
static::updated(function ($category) {
if ($category->wasChanged('tax_percentage')) {
CategoryTaxChanged::dispatch($category);
}
});
}
/**
* Attributes (properties) of Category
*/
public function properties(): BelongsToMany
{
return $this->belongsToMany(Attribute::class, 'attributes');
}
/**
* Category Products
*/
public function products(): MorphToMany
{
return $this->morphToMany(Product::class, 'productable', 'product_has_relations');
}
/**
* Scope for visable models
*/
public function scopeEnabled(Builder $query): void
{
$query->where('is_visible', true);
}
/**
* Color attribute
*/
public function color(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('color'),
);
}
/**
* Category's Parent name
*
* @return ?string
*/
public function parentName(): Attribute
{
return Attribute::make(
get: fn () => $this->parent?->name
);
}
/**
* Check if model belongs to Market
*/
public function isMarketCategory(): bool
{
return $this->type === 'market';
}
/**
* Category's Products webpage
*/
public function productsPage(): string
{
return route('web.categories.products', ['category' => $this->slug]);
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace App\Models\Ecommerce\Product\Collection;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Spatie\EloquentSortable\Sortable;
use Spatie\EloquentSortable\SortableTrait;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
use Spatie\Translatable\HasTranslations;
use Staudenmeir\EloquentEagerLimitXLaravelAdjacencyList\Eloquent\HasEagerLimitAndRecursiveRelationships;
class Collection extends Model implements HasMedia, Sortable
{
use HasEagerLimitAndRecursiveRelationships;
use HasFactory;
use HasSlug;
use HasTranslations;
use InteractsWithMedia;
use SortableTrait;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'name',
'slug',
'description',
'sort_order',
'seo_title',
'seo_description',
'is_visible',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['name', 'description'];
/**
* Translatable fields
*
* @var array<string, mixed>
*/
public $sortable = [
'order_column_name' => 'sort_order',
'sort_when_creating' => true,
];
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug');
}
/**
* Register Media Collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('uploads')
->singleFile()
->acceptsMimeTypes(['image/jpg', 'image/jpeg', 'image/png'])
->useFallbackUrl(
sprintf('%s/logo-space.png', config('app.url'))
);
}
/**
* Register Media Conversions
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb200x200')
->fit(Manipulations::FIT_CONTAIN, 200, 200);
$this->addMediaConversion('thumb400x400')
->fit(Manipulations::FIT_CONTAIN, 400, 400);
$this->addMediaConversion('thumb720x720')
->fit(Manipulations::FIT_CONTAIN, 720, 720);
$this->addMediaConversion('thumb800x800')
->fit(Manipulations::FIT_CONTAIN, 800, 800);
$this->addMediaConversion('thumb1200x1200')
->fit(Manipulations::FIT_CONTAIN, 1200, 1200);
$this->addMediaConversion('thumb288x431')
->fit(Manipulations::FIT_CONTAIN, 288, 431);
$this->addMediaConversion('thumb270x350')
->fit(Manipulations::FIT_CONTAIN, 270, 350);
}
/**
* Products
*/
public function products(): MorphToMany
{
return $this->morphToMany(Product::class, 'productable', 'product_has_relations');
}
/**
* Brand's Products web page
*/
public function productsPage(): string
{
return route('web.collections.products', ['collection' => $this->slug]);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Models\Ecommerce\Product\Coupon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Coupon extends Model
{
use HasFactory;
/**
* Discount by percentage (%)
*/
public const DISCOUNT_PERCENTAGE = 'percentage';
/**
* Discount by fixed amount (ex: -10 TMT)
*/
public const DISCOUNT_FIXED = 'fixed_amount';
/**
* @var array<int, string>
*/
protected $fillable = [
'code',
'discount_type',
'discount_value',
'notes',
'is_active',
];
/**
* Discount types
*/
public static function discountTypes(): array
{
return [
self::DISCOUNT_PERCENTAGE => __('Percentage'),
self::DISCOUNT_FIXED => __('Fixed Amount'),
];
}
/**
* Default discount type
*/
public static function defaultDiscountType(): string
{
return self::DISCOUNT_PERCENTAGE;
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Models\Ecommerce\Product\Favorite;
use App\Models\Ecommerce\Product\Product\Product;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Favorite extends Model
{
use HasFactory;
/**
* The table associated with the model.
*/
protected $table = 'favorites';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'user_id',
'product_id',
];
/**
* Favourite Product
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class, 'product_id', 'id');
}
/**
* Favourite model User (Owner)
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace App\Models\Ecommerce\Product\Inventory;
use App\Models\Ecommerce\Channel\Channel;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
class Inventory extends Model
{
use HasFactory;
use HasSlug;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'name',
'code',
'description',
'email',
'phone_number',
'street_address',
'zipcode',
'region',
'province_id',
'longitude',
'latitude',
'is_default',
'channel_id',
'shareable',
];
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('code')
->doNotGenerateSlugsOnUpdate();
}
/**
* User Channel
*/
public function channel(): BelongsTo
{
return $this->belongsTo(Channel::class);
}
/**
* Products
*/
public function products()
{
return $this->belongsToMany(Product::class)->withPivot('stock');
}
/**
* Tmpost default inventory
*/
public static function tmpostDefault(): Inventory
{
return static::where('code', 'tmpost')->first();
}
/**
* History
*/
public function history(): HasMany
{
return $this->hasMany(InventoryHistory::class, 'inventory_id');
}
/**
* History removed
*/
public function historyRemoved(): HasMany
{
return $this->hasMany(InventoryHistoryRemoved::class, 'inventory_id');
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Models\Ecommerce\Product\Inventory;
use App\Models\Concerns\HasSchemalessAttributes;
use App\Models\Ecommerce\Channel\Channel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class InventoryHistory extends Model
{
use HasFactory;
use HasSchemalessAttributes;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'date',
'inventory_id',
'channel_id',
'notes',
'total',
'options',
];
/**
* Attributes that are casted
*/
protected $casts = [
'date' => 'date',
];
/**
* Channel
*/
public function channel(): BelongsTo
{
return $this->belongsTo(Channel::class);
}
/**
* Inventory
*/
public function inventory(): BelongsTo
{
return $this->belongsTo(Inventory::class);
}
/**
* Items
*/
public function items(): HasMany
{
return $this->hasMany(InventoryHistoryItem::class, 'inventory_history_id');
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Models\Ecommerce\Product\Inventory;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class InventoryHistoryItem extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'inventory_history_id',
'product_id',
'quantity',
'total',
'cost_amount',
];
/**
* Inventory history
*/
public function inventoryHistory(): BelongsTo
{
return $this->belongsTo(InventoryHistory::class, 'inventory_history_id');
}
/**
* Product
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace App\Models\Ecommerce\Product\Inventory;
use App\Models\Concerns\HasSchemalessAttributes;
use App\Models\Ecommerce\Channel\Channel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class InventoryHistoryRemoved extends Model
{
use HasFactory;
use HasSchemalessAttributes;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'date',
'inventory_id',
'channel_id',
'notes',
'total',
'options',
];
/**
* Attributes that are casted
*/
protected $casts = [
'date' => 'date',
];
/**
* Channel
*/
public function channel(): BelongsTo
{
return $this->belongsTo(Channel::class);
}
/**
* Inventory
*/
public function inventory(): BelongsTo
{
return $this->belongsTo(Inventory::class);
}
/**
* Items
*/
public function items(): HasMany
{
return $this->hasMany(InventoryHistoryRemovedItem::class);
}
}

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Models\Ecommerce\Product\Inventory;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class InventoryHistoryRemovedItem extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'inventory_history_removed_id',
'product_id',
'quantity',
'total',
'cost_amount',
];
/**
* Inventory history
*/
public function inventoryHistoryRemoved(): BelongsTo
{
return $this->belongsTo(InventoryHistoryRemoved::class, 'inventory_history_removed_id');
}
/**
* Product
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace App\Models\Ecommerce\Product\Order\Concerns;
trait HasPayments
{
/**
* Total price for order
*/
public function total(): float
{
return $this->items->sum('total');
}
/**
* Return total order with shipping.
*/
public function fullPriceWithShipping(): string
{
return floatval($this->total()) + floatval($this->shippingPrice()) + floatval($this->additional_tax);
}
/**
* Formatted payment type
*/
public function formattedPaymentType(): string
{
return $this->paymentType?->name;
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Models\Ecommerce\Product\Order\Concerns;
use App\Models\Ecommerce\Product\Order\Shipping\OrderShipping;
trait HasShipping
{
/**
* Shipping price
*/
public function shippingPrice(): int
{
return intval($this->shipping_price) ?: OrderShipping::priceFor($this->shipping_method);
}
/**
* Formatted shipping method
*/
public function formattedShippingMethod(): string
{
return OrderShipping::formattedShippingMethod($this->shipping_method);
}
/**
* Full address for the order
*/
public function fullAddress(): string
{
return OrderShipping::fullAddress($this->region, $this->province_id, $this->customer_address);
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Models\Ecommerce\Product\Order\Concerns;
use App\Models\Ecommerce\Product\Order\Status\OrderStatus;
trait HasStatus
{
/**
* Return the correct order status formatted.
*/
public function formatted_status(): string
{
return OrderStatus::formattedStatusFor($this->status);
}
/**
* Can order be cancelled?
*/
public function canBeCancelled(): bool
{
return $this->isPending();
}
/**
* Check if order is pending
*/
public function isPending(): bool
{
return $this->status === OrderStatus::PENDING;
}
/**
* Check if order is registered
*/
public function isRegistered(): bool
{
return $this->status === OrderStatus::REGISTER;
}
/**
* Check if order is paid
*/
public function isPaid(): bool
{
return $this->status === OrderStatus::PAID;
}
/**
* Check if order is completed
*/
public function isCompleted(): bool
{
return $this->status === OrderStatus::COMPLETED;
}
/**
* Check if order is completed
*/
public function isCancelled(): bool
{
return $this->status === OrderStatus::CANCELLED;
}
/**
* Mark order as paid
*/
public function markAsPaid(): void
{
$this->update(['status' => OrderStatus::PAID]);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace App\Models\Ecommerce\Product\Order;
use App\Models\Ecommerce\Product\Order\Concerns\HasPayments;
use App\Models\Ecommerce\Product\Order\Concerns\HasShipping;
use App\Models\Ecommerce\Product\Order\Concerns\HasStatus;
use App\Models\System\Settings\Location\Province;
use App\Models\System\Settings\Payments\PaymentType;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Order extends Model
{
use HasFactory;
use HasPayments;
use HasShipping;
use HasStatus;
use SoftDeletes;
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'number',
'status',
'shipping_method',
'shipping_price',
'payment_type_id',
'notes',
'customer_name',
'customer_phone',
'customer_address',
'delivery_time',
'delivery_at',
'region',
'user_id',
'additional_tax',
'province_id',
'source_app',
];
/**
* The attributes that should be cast.
*/
protected $casts = [
'delivery_at' => 'date',
];
/**
* User (customer)
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Related Province
*/
public function province(): BelongsTo
{
return $this->belongsTo(Province::class);
}
/**
* Order items
*/
public function items(): HasMany
{
return $this->hasMany(OrderItem::class);
}
/**
* Payment types
*/
public function paymentType(): BelongsTo
{
return $this->belongsTo(PaymentType::class, 'payment_type_id');
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Models\Ecommerce\Product\Order;
use App\Models\Ecommerce\Channel\Channel;
use App\Models\Ecommerce\Product\Product\Product;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class OrderItem extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'product_name',
'product_id',
'order_id',
'channel_id',
'quantity',
'unit_price_amount',
'unit_cost_amount',
];
/**
* Related order (parent)
*/
public function order(): BelongsTo
{
return $this->belongsTo(Order::class, 'order_id');
}
/**
* Related product
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class, 'product_id', 'id');
}
/**
* Related Channel
*/
public function channel(): BelongsTo
{
return $this->belongsTo(Channel::class, 'channel_id', 'id');
}
/**
* Total price
*/
public function total(): Attribute
{
return Attribute::make(
get: fn () => (float) $this->unit_price_amount * (int) $this->quantity
);
}
}

View File

@@ -0,0 +1,88 @@
<?php
namespace App\Models\Ecommerce\Product\Order\Payment;
use App\Models\System\Settings\Payments\PaymentType;
use App\Repositories\System\Cache\CacheRepository;
class OrderPayment
{
/**
* Cash
*/
public const CASH = 'cash';
/**
* ATM
*/
public const ATM = 'atm';
/**
* Online (halkbank)
*/
public const ONLINE = 'online';
/**
* Online (halkbank)
*/
public const HALK_BANK = 'halk_bank';
/**
* Online (senagat)
*/
public const SENAGAT_BANK = 'senegat_bank';
/**
* Online (rysgal)
*/
public const RYSGAL_BANK = 'rysgal_bank';
/**
* Online (wneshka)
*/
public const WNESHKA_BANK = 'wneshka_bank';
/**
* Default payment value
*/
public static function default(): ?string
{
return CacheRepository::make(
name: 'cs-nova-models-default-order-payment',
value: fn () => PaymentType::where('code', 'cash')->pluck('id')->first(),
time: 60 * 60 * 24
);
}
/**
* Payment values
*/
public static function values(): array
{
return CacheRepository::make(
name: 'cs-nova-models-order-payments',
value: fn () => PaymentType::pluck('name', 'id')->toArray()
);
}
/**
* Old but good types
*
* @return array<int, string>
*/
public static function oldButGoodTypes(): array
{
return [
static::ATM => __('ATM'),
static::CASH => __('Cash'),
];
}
/**
* Formatted payment type for given "type"
*/
public static function formattedPaymentTypeFor(string $type): string
{
return static::values()[$type] ?? self::CASH;
}
}

View File

@@ -0,0 +1,201 @@
<?php
namespace App\Models\Ecommerce\Product\Order\Shipping;
use App\Models\Ecommerce\Product\Order\Order;
use App\Models\System\Settings\Location\Province;
use App\Models\System\Settings\Location\Region;
class OrderShipping
{
/*
* Shipping methods
*
* standart - basic
* express - fast
* self_pickup - self pickup
* region - deliver to regions (mr, ah, dz, lb, bn)
*/
/**
* Standart shipping
*/
public const STANDART = 'standart';
/**
* Express shipping
*/
public const EXPRESS = 'express';
/**
* On own - self pickup
*/
public const SELF_PICKUP = 'self_pickup';
/**
* Region - shipping to regions (mr, ah, ...)
*/
public const REGION = 'region';
/*
* Shipping method prices
*
* standart - 20
* express - 30
* self pickup - 0
* region - 40
*/
/**
* Standart shipping price
*/
public const STANDART_PRICE = 20;
/**
* Express shipping price
*/
public const EXPRESS_PRICE = 30;
/**
* Self pickup shipping price
*/
public const SELF_PICKUP_PRICE = 0;
/**
* Region shipping price
*/
public const REGION_PRICE = 40;
/**
* Order delivery times
*
* morning
* evening
*/
/**
* Morning time 09 - 12:00
*/
public const MORNING = '09:00 - 12:00';
/**
* Evening time 13:00 - 18:00
*/
public const EVENING = '13:00 - 18:00';
/**
* Default shipping value
*/
public static function default(): string
{
return static::STANDART;
}
/**
* Default shipping time value
*/
public static function defaultTime(): string
{
return static::EVENING;
}
/**
* Shipping values
*/
public static function values(): array
{
return [
self::STANDART => __('Standart'),
self::EXPRESS => __('Express'),
self::SELF_PICKUP => __('Self Pickup'),
self::REGION => __('Region'),
];
}
/**
* Prices
*/
public static function prices(): array
{
return [
self::STANDART => self::STANDART_PRICE,
self::EXPRESS => self::EXPRESS_PRICE,
self::SELF_PICKUP => self::SELF_PICKUP_PRICE,
self::REGION => self::REGION_PRICE,
];
}
/**
* Shipping times
*/
public static function times(): array
{
return [
self::MORNING => self::MORNING,
self::EVENING => self::EVENING,
];
}
/**
* Get price for specific shipping
*/
public static function priceFor(string $type): int
{
return static::prices()[$type] ?? self::STANDART_PRICE;
}
/**
* Order shipping price
*
* @param Order $order
*/
public static function orderShippingPrice($order): int
{
if ($order->region === Region::AG) {
return static::priceFor($order->shipping_method);
}
if (in_array($order->province_id, [
12,
43,
45,
83,
56,
22,
])) {
return 30;
}
return static::REGION_PRICE;
}
/**
* Formatted shipping method
*/
public static function formattedShippingMethod(string $shippingMethod): string
{
return static::values()[$shippingMethod] ?? __('Standart');
}
/**
* Get full address
*/
public static function fullAddress($region, $province, $customer_address): string
{
$address = '';
if ($region) {
$address .= Region::labelFor($region);
}
if ($province = Province::where('id', $province)->first('name')) {
$address .= ', '.$province->name;
}
if ($customer_address) {
$address .= ','.$customer_address;
}
return $address;
}
}

View File

@@ -0,0 +1,89 @@
<?php
namespace App\Models\Ecommerce\Product\Order\Status;
class OrderStatus
{
/**
* Pending orders are brand new orders that have not been processed yet.
*/
public const PENDING = 'pending';
/**
* Orders that has been registered..
*/
public const REGISTER = 'register';
/**
* Orders that has been paid..
*/
public const PAID = 'paid';
/**
* Orders fulfilled completely.
*/
public const COMPLETED = 'completed';
/**
* Order that has been cancelled.
*/
public const CANCELLED = 'cancelled';
/**
* Default status value
*/
public static function default(): string
{
return static::PENDING;
}
/**
* Status Values
*/
public static function values(): array
{
return [
self::PENDING => __('Pending'),
self::REGISTER => __('Registered'),
self::PAID => __('Paid'),
self::COMPLETED => __('Completed'),
self::CANCELLED => __('Cancelled'),
];
}
/**
* Formatted status for given "status"
*/
public static function formattedStatusFor(string $status): string
{
return static::values()[$status] ?? __('None');
}
/**
* Tailwind
*/
public static function classes(): array
{
return [
self::PENDING => 'warning',
self::REGISTER => 'info',
self::PAID => 'primary',
self::COMPLETED => 'success',
self::CANCELLED => 'danger',
];
}
/**
* HEX Colors
*/
public static function colors(): array
{
return [
self::PENDING => '#F5573B',
self::REGISTER => '#F2CB22',
self::PAID => '#098F56',
self::COMPLETED => '#8FC15D',
self::CANCELLED => '#d70206',
];
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace App\Models\Ecommerce\Product\Product\Concerns;
use Laravel\Nova\Nova;
trait ProductFrontEndHelpers
{
/**
* Show page
*/
public function showPage(): string
{
return route('web.products.show', ['product' => $this->slug]);
}
/**
* Nova page
*/
public function novaPage(): string
{
return Nova::url('/resources/products');
}
/**
* Nova detail page
*/
public function novaDetailPage(): string
{
return Nova::url('/resources/products/'.$this->id);
}
}

View File

@@ -0,0 +1,70 @@
<?php
namespace App\Models\Ecommerce\Product\Product\Concerns;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
trait ProductMedia
{
/**
* Media collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('uploads')
->useFallbackUrl(url('/assets/web/images/05.jpg'));
}
/**
* Media Conversions
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb200x200')
->fit(Manipulations::FIT_CONTAIN, 200, 200);
$this->addMediaConversion('thumb400x400')
->fit(Manipulations::FIT_CONTAIN, 400, 400);
$this->addMediaConversion('thumb720x720')
->fit(Manipulations::FIT_CONTAIN, 720, 720);
$this->addMediaConversion('thumb800x800')
->fit(Manipulations::FIT_CONTAIN, 800, 800);
$this->addMediaConversion('thumb1200x1200')
->fit(Manipulations::FIT_CONTAIN, 1200, 1200);
$this->addMediaConversion('thumb288x431')
->fit(Manipulations::FIT_CONTAIN, 288, 431);
$this->addMediaConversion('thumb270x350')
->fit(Manipulations::FIT_CONTAIN, 270, 350);
}
/**
* Thumbnail
*/
public function thumbnail(string $size = '200x200'): string
{
return $this->getFirstMediaUrl('uploads', 'thumb'.$size);
}
/**
* Get image when hovered (returns second image)
*/
public function getHoverImage(string $size = '270x350'): string
{
$media = $this->getMedia('uploads');
$image_count = $media->count();
if ($image_count == 0) {
return '';
}
return ($image_count == 1)
? $media[0]->getUrl('thumb'.$size)
: $media[1]->getUrl('thumb'.$size);
}
}

View File

@@ -0,0 +1,436 @@
<?php
namespace App\Models\Ecommerce\Product\Product\Concerns;
use App\Models\Ecommerce\Product\Property\ProductProperty;
use Illuminate\Support\Facades\Blade;
trait ProductProperties
{
/**
* Check if given product has given property
*/
public function containsProperty(string $property): bool
{
return $this->properties->pluck('attribute.slug')->contains($property);
}
/**
* Get product property
*/
public function getProperty(string $name): ?ProductProperty
{
return $this->properties->first(fn ($property) => $property->attribute->slug === $name);
}
/**
* Check if product has color variants
*/
public function hasColorVariants(): bool
{
return $this->containsProperty('colour');
}
/**
* Get product color property
*/
public function getColorProperty(): ?ProductProperty
{
return $this->getProperty('colour');
}
/**
* Get product color property
*/
public function getSizeProperty(): ?ProductProperty
{
return $this->getProperty('size');
}
/**
* Get color value of product
*/
public function getColorValue(): ?string
{
$colorProperty = $this->getColorProperty();
return $colorProperty ? $colorProperty->values->first()?->real_value : null;
}
/**
* Get size value
*/
public function getSizeValue(): ?string
{
$sizeProperty = $this->getSizeProperty();
return $sizeProperty ? $sizeProperty->values->first()?->real_value : null;
}
/**
* Check if product has a color.
*/
public function hasColor(): bool
{
return $this->hasPropertyWithSlug('colour');
}
/**
* Check if product has a size.
*/
public function hasSize(): bool
{
return $this->hasPropertyWithSlug('size');
}
/**
* Color property
*/
public function getColor(): string
{
$product = 'productColor'.$this->id;
if (temp_cache($product)) {
return temp_cache($product);
}
cache()->put(
key: 'productColor'.$this->id,
value: $this->getPropertyValueBySlug('colour'),
);
return temp_cache($product);
}
/**
* Size property
*/
public function getSize(): string
{
$product = 'productSize'.$this->id;
if (temp_cache($product)) {
return temp_cache($product);
}
cache()->put(
'productSize'.$this->id,
$this->getPropertyValueBySlug('size'),
);
return temp_cache($product);
}
/**
* Color variants
*/
public function colorVariants(): array
{
if (temp_cache('productColorVariants')) {
return temp_cache('productColorVariants');
}
$this->loadMissing(['media', 'properties', 'variations' => ['media', 'properties']]);
$colorVariants = collect();
// Add parent product
$colorVariants->push([
'slug' => $this->slug,
'id' => $this->id,
'image' => $this->thumbnail(),
'color' => $this->getColor(),
]);
$this->variations->each(function ($variation) use ($colorVariants) {
$colorVariants->push([
'slug' => $variation->slug,
'id' => $variation->id,
'image' => $variation->thumbnail(),
'color' => $variation->getColor(),
]);
});
cache()->put(
'productColorVariants',
$colorVariants->uniqueStrict('color')->toArray(),
);
return $colorVariants->toArray();
}
/**
* Check if a property with the given attribute slug exists
*/
protected function hasPropertyWithSlug(string $slug): bool
{
if ($this->relationLoaded('properties')) {
return $this->checkLoadedPropertiesForSlug($slug);
}
return $this->checkDatabaseForPropertySlug($slug);
}
/**
* Check loaded properties for attribute slug
*/
protected function checkLoadedPropertiesForSlug(string $slug): bool
{
foreach ($this->properties as $property) {
if ($property->attribute->slug === $slug) {
return true;
}
}
return false;
}
/**
* Check database for attribute slug
*/
protected function checkDatabaseForPropertySlug(string $slug): bool
{
return $this->properties()
->where('attribute.slug', $slug)
->exists();
}
/**
* Get property value by slug
*/
public function getPropertyValueBySlug(string $slug): string
{
return $this->properties->firstWhere('attribute.slug', 'colour')->values->first()->real_value ?? '';
}
/**
* Get size variants available for given color
*/
public function getSizeVariantsForColor(): array
{
if (temp_cache('productSizeVariantsForColor')) {
return temp_cache('productSizeVariantsForColor');
}
if ($this->parent_id) {
return $this->childSizeVariantsForColor();
}
$this->loadMissing(['properties', 'variations', 'variations.properties']);
$properties = $this->properties;
$variations = $this->variations;
$productSize = [];
$matchingProducts = [];
foreach ($properties as $property) {
if ($property->attribute->slug === 'size') {
$productSize = $property->values->first()->real_value;
$productSize = [
'slug' => $this->slug,
'id' => $this->id,
'size' => $productSize,
'parent_id' => null,
];
}
if ($property->attribute->slug === 'colour') {
// Color found
$productColor = $property->values->first()->real_value;
// Get All Sizes
$variationAllSizes = [];
// Iterate over to find same colors in variants
foreach ($variations as $variation) {
foreach ($variation->properties as $variationProperty) {
if ($variationProperty->attribute->slug === 'size') {
$productSize = $variationProperty->values->first()->real_value;
$variationAllSizes[] = [
'slug' => $variation->slug,
'id' => $variation->id,
'parent_id' => $variation->parent_id,
'size' => $productSize,
];
}
if ($variationProperty->attribute->slug === 'colour') {
// If colors match, append to array
$variationColor = $variationProperty->values->first()->real_value;
if ($productColor === $variationColor) {
$matchingProducts[] = [
'slug' => $variation->slug,
'id' => $variation->id,
'color' => $productColor,
];
}
}
}
}
} /* Color end */
}
$result = collect();
$result->push($productSize);
collect($variationAllSizes)->whereIn('id', collect($matchingProducts)->pluck('id'))
->each(function ($variationSize) use ($result) {
$result->push($variationSize);
});
cache()->put(
'productSizeVariantsForColor',
$result->toArray(),
);
return $result->toArray();
}
public function childSizeVariantsForColor(): array
{
$this->loadMissing(['properties']);
$properties = $this->properties;
$parent = $this->parent;
$parent->load(['properties', 'variations', 'variations.properties']);
$variations = $parent->variations;
$productSize = [];
$matchingProducts = [];
foreach ($properties as $property) {
if ($property->attribute->slug === 'size') {
$productSize = $property->values->first()->real_value;
$productSize = [
'slug' => $this->slug,
'id' => $this->id,
'size' => $productSize,
'parent_id' => $this->parent_id,
];
}
if ($property->attribute->slug === 'colour') {
// Color found
$productColor = $property->values->first()->real_value;
// Get All Sizes
$variationAllSizes = [];
// Iterate over to find same colors in variants
foreach ($variations as $variation) {
if ($variation->id === $this->id) {
continue;
}
foreach ($variation->properties as $variationProperty) {
if ($variationProperty->attribute->slug === 'size') {
$productSize = $variationProperty->values->first()->real_value;
$variationAllSizes[] = [
'slug' => $variation->slug,
'id' => $variation->id,
'parent_id' => $variation->parent_id,
'size' => $productSize,
];
}
if ($variationProperty->attribute->slug === 'colour') {
// If colors match, append to array
$variationColor = $variationProperty->values->first()->real_value;
if ($productColor === $variationColor) {
$matchingProducts[] = [
'slug' => $variation->slug,
'id' => $variation->id,
'color' => $productColor,
];
}
}
}
}
} /* Color end */
}
$result = collect();
$result->push($productSize);
collect($variationAllSizes)->whereIn('id', collect($matchingProducts)->pluck('id'))
->each(function ($variationSize) use ($result) {
$result->push($variationSize);
});
return $result->toArray();
}
/**
* All values of product properties
*/
public function allValuesOfProperties(): array
{
return $this->properties->flatMap(function ($property) {
return $property->values->map(function ($value) {
return $value->real_value;
})->filter();
})->all();
}
/**
* Name with properties
*/
public function nameWithProperties(): string
{
$properties = $this->allValuesOfProperties();
return sprintf(
'%s %s %s',
$this->name,
count($properties) > 0 ? '-' : '',
Blade::render('
@foreach($datas as $data)
<strong>{{ $data }}{{ $loop->last ? "" : "," }}</strong>
@endforeach
', ['datas' => $properties])
);
}
public function getSizeVariants(): array
{
$sizeVariants = [];
// Add parent product
$sizeVariants[] = [
'slug' => $this->slug,
'id' => $this->id,
'image' => $this->thumbnail(),
'images_hd' => $this->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb720x720')),
'size' => $this->getSizeValue(),
];
$this->variations->each(function ($variation) use (&$sizeVariants) {
$variationSize = $variation->getSizeValue();
if ($variationSize && ! keyValueExistsInArray(datas: $sizeVariants, key: 'size', value: $variationSize)) {
$sizeVariants[] = [
'slug' => $variation->slug,
'id' => $variation->id,
'image' => $variation->thumbnail(),
'images_hd' => $variation->getMedia('uploads')->map(fn ($media) => $media->getUrl('thumb720x720')),
'size' => $variationSize,
];
}
});
return $sizeVariants;
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace App\Models\Ecommerce\Product\Product\Concerns;
use App\Models\Common\Comment;
use App\Models\Ecommerce\Channel\Channel;
use App\Models\Ecommerce\Product\Brand\Brand;
use App\Models\Ecommerce\Product\Cart\Cart;
use App\Models\Ecommerce\Product\Category\Category;
use App\Models\Ecommerce\Product\Collection\Collection;
use App\Models\Ecommerce\Product\Inventory\Inventory;
use App\Models\Ecommerce\Product\Product\Product;
use App\Models\Ecommerce\Product\Property\ProductProperty;
use App\Models\Ecommerce\Product\Review\Review;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
trait ProductRelationships
{
/**
* Product variations
*/
public function variations(): HasMany
{
return $this->hasMany(Product::class, 'parent_id');
}
/**
+ * Parent
+ */
public function parent(): BelongsTo
{
return $this->belongsTo(Product::class, 'parent_id');
}
/**
* Related Channels
*/
public function channels(): MorphToMany
{
return $this->morphedByMany(Channel::class, 'productable', 'product_has_relations');
}
/**
* Firt channel
*/
public function owner(): ?Channel
{
return $this->relationLoaded('channels')
? $this->channels->first()
: $this->channels()->first();
}
/**
* Related products (similar)
*/
public function relatedProducts(): MorphToMany
{
return $this->morphedByMany(Product::class, 'productable', 'product_has_relations');
}
/**
* Related categories
*/
public function categories(): MorphToMany
{
return $this->morphedByMany(Category::class, 'productable', 'product_has_relations');
}
/**
* Related Collections
*/
public function collections(): MorphToMany
{
return $this->morphedByMany(Collection::class, 'productable', 'product_has_relations');
}
/**
* Related Brand
*/
public function brand(): BelongsTo
{
return $this->belongsTo(Brand::class, 'brand_id');
}
/**
* Product Properties, same as attribtues
*
* (size, color, material, ..)
*/
public function properties(): HasMany
{
return $this->hasMany(ProductProperty::class);
}
/**
* Related inventories
*/
public function inventories(): BelongsToMany
{
return $this->belongsToMany(Inventory::class)
->withPivot('stock');
}
/**
* Carts
*/
public function carts(): BelongsTo
{
return $this->belongsTo(Cart::class);
}
/**
* Product's review
*/
public function reviews(): HasMany
{
return $this->hasMany(Review::class);
}
/**
* Product's comments
*/
public function comments(): BelongsToMany
{
return $this->belongsToMany(Comment::class);
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Models\Ecommerce\Product\Product\Concerns;
trait ProductScopeQueries
{
/**
* Visable products scope
*
* @param mixed $query
*/
public function scopeVisable($query): void
{
$query->where('is_visible', true);
}
}

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Models\Ecommerce\Product\Product\Concerns;
trait ProductStates
{
/**
* Check if product is new (one week range)
*/
public function isNew(): bool
{
return $this->created_at->greaterThan(now()->subWeek());
}
}

View File

@@ -0,0 +1,16 @@
<?php
namespace App\Models\Ecommerce\Product\Product\Concerns;
use Illuminate\Database\Eloquent\Relations\MorphMany;
trait ProductStock
{
/**
* Inventory history
*/
public function inventoryHistories(): MorphMany
{
return $this->morphMany(InventoryHistory::class, 'stockable')->orderBy('created_at', 'desc');
}
}

View File

@@ -0,0 +1,179 @@
<?php
namespace App\Models\Ecommerce\Product\Product\Concerns;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Spatie\Sluggable\SlugOptions;
trait ProductTransformers
{
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug');
}
/**
* Get discount difference
*
* @return int|float
*/
public function discountDifference(): Attribute
{
return Attribute::make(get: fn () => $this->caluculateDiscountDifference());
}
/**
* Discount percentage
*/
public function discountPercentage()
{
return number_format(($this->caluculateDiscountDifference() * 100) / $this->old_price_amount);
}
/**
* Caluculate Discount Difference
*/
public function caluculateDiscountDifference(int|float|null $price_amount = null): float
{
return $this->old_price_amount ? $this->old_price_amount - ($price_amount ?: $this->price_amount) : 0;
}
/**
* Full name
*/
public function fullName(): Attribute
{
return Attribute::make(
get: function () {
$brand = $this->brand ? "({$this->brand->name})" : '';
return $this->name.$brand.$this->colour.$this->size;
}
);
}
/**
* @param bool backorder
*/
public function backOrder(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('back_order')
);
}
/**
* @param string weight_value
*/
public function weightValue(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('weight_value')
);
}
/**
* @param string weight_unit
*/
public function weightUnit(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('weight_unit')
);
}
/**
* @param string height_value
*/
public function heightValue(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('height_value')
);
}
/**
* @param string height_unit
*/
public function heightUnit(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('height_unit')
);
}
/**
* @param string width_value
*/
public function widthValue(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('width_value')
);
}
/**
* @param string width_unit
*/
public function widthUnit(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('width_unit')
);
}
/**
* @param string depth_value
*/
public function depthValue(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('depth_value')
);
}
/**
* @param string depth_unit
*/
public function depthUnit(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('depth_unit')
);
}
/**
* @param string volume_value
*/
public function volumeValue(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('volume_value')
);
}
/**
* @param string volume_unit
*/
public function volumeUnit(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('volume_unit')
);
}
/**
* Flexible Content
*/
// public function flexibleContent(): Attribute
// {
// return Attribute::make(
// get: fn () => $this->flexible('flexible-content')
// );
// }
}

View File

@@ -0,0 +1,121 @@
<?php
namespace App\Models\Ecommerce\Product\Product;
use App\Models\Concerns\HasSchemalessAttributes;
use App\Models\Ecommerce\Product\Product\Concerns\ProductFrontEndHelpers;
use App\Models\Ecommerce\Product\Product\Concerns\ProductMedia;
use App\Models\Ecommerce\Product\Product\Concerns\ProductProperties;
use App\Models\Ecommerce\Product\Product\Concerns\ProductRelationships;
use App\Models\Ecommerce\Product\Product\Concerns\ProductScopeQueries;
use App\Models\Ecommerce\Product\Product\Concerns\ProductStates;
use App\Models\Ecommerce\Product\Product\Concerns\ProductStock;
use App\Models\Ecommerce\Product\Product\Concerns\ProductTransformers;
use CyrildeWit\EloquentViewable\Contracts\Viewable;
use CyrildeWit\EloquentViewable\InteractsWithViews;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\Sluggable\HasSlug;
class Product extends Model implements HasMedia, Viewable
{
/**
* Has factory (laravel default)
*/
use HasFactory;
/**
* Has Schemaless Attributes (spatie/laravel-schemaless-attributes)
*/
use HasSchemalessAttributes;
/**
* Has Slug (spatie/laravel-sluggable)
*/
use HasSlug;
/**
* Interacts with media (spatie/laravel-medialibrary)
*/
use InteractsWithMedia;
/**
* Interacts with views (cyrildewit/eloquent-viewable)
*/
use InteractsWithViews;
/**
* Product helpers for front end
*/
use ProductFrontEndHelpers;
/**
* Media interactions
*/
use ProductMedia {
ProductMedia::registerMediaCollections insteadof InteractsWithMedia;
ProductMedia::registerMediaConversions insteadof InteractsWithMedia;
}
/**
* Product properties (attributes[EAV])
*/
use ProductProperties;
/**
* Product relationships
*/
use ProductRelationships;
/**
* Product scope queries
*/
use ProductScopeQueries;
/**
* Product states (new, featured, ...)
*/
use ProductStates;
/**
* Products stocks
*/
use ProductStock;
/**
* Interacts with media (whitecube/nova-flexible-content)
*/
// use HasFlexible;
/**
* Product Data Transformers
*/
use ProductTransformers;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'id',
'parent_id',
'brand_id',
'name',
'slug',
'description',
'sku',
'barcode',
'stock',
'security_stock',
'cost_amount',
'price_amount',
'old_price_amount',
'seo_title',
'seo_description',
'options',
'is_visible',
'colour',
'size',
];
}

View File

@@ -0,0 +1,103 @@
<?php
namespace App\Models\Ecommerce\Product\Property;
use App\Models\Ecommerce\Product\Category\Category;
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 Spatie\EloquentSortable\Sortable;
use Spatie\EloquentSortable\SortableTrait;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
use Spatie\Translatable\HasTranslations;
class Attribute extends Model implements Sortable
{
use HasFactory;
use HasSlug;
use HasTranslations;
use SortableTrait;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'name',
'slug',
'description',
'type',
'is_visible',
'is_required',
'is_searchable',
'is_filterable',
'sort_order',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['name', 'description'];
/**
* Translatable fields
*
* @var array<string, mixed>
*/
public $sortable = [
'order_column_name' => 'sort_order',
'sort_when_creating' => true,
];
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('slug');
}
/**
* Attribute values (color: red, black, ...)
*/
public function values(): HasMany
{
return $this->hasMany(AttributeValue::class);
}
/**
* Attribute Category
*/
public function category(): BelongsTo
{
return $this->belongsTo(Category::class);
}
/**
* Attributes categories as belongs-to-many
*/
public function categories(): BelongsToMany
{
return $this->belongsToMany(Category::class);
}
/**
* Return available fields types.
*
* @return array<string, string>
*/
public static function typesFields(): array
{
return [
'text' => __('Text'),
'number' => __('Number'),
'select' => __('Select option'),
];
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Models\Ecommerce\Product\Property;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\EloquentSortable\Sortable;
use Spatie\EloquentSortable\SortableTrait;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
use Spatie\Translatable\HasTranslations;
class AttributeValue extends Model implements Sortable
{
use HasFactory;
use HasSlug;
use HasTranslations;
use SortableTrait;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'value',
'key',
'attribute_id',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['value'];
/**
* Translatable fields
*
* @var array<string, mixed>
*/
public $sortable = [
'order_column_name' => 'sort_order',
'sort_when_creating' => true,
];
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->usingSeparator('_')
->saveSlugsTo('key');
}
/**
* Attribute
*/
public function attribute(): BelongsTo
{
return $this->belongsTo(Attribute::class, 'attribute_id');
}
}

View File

@@ -0,0 +1,72 @@
<?php
namespace App\Models\Ecommerce\Product\Property;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class ProductAttributeValue extends Model
{
use HasFactory;
/**
* Table name
*/
protected $table = 'attribute_value_product_attribute';
/**
* Indicates if the model should be timestamped.
*/
public $timestamps = false;
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'product_attribute_id',
'attribute_value_id',
'product_custom_value',
];
/**
* The relations to eager load on every query.
*/
protected $with = ['value'];
/**
* The accessors to append to the model's array form.
*/
protected $appends = ['real_value'];
/**
* Return exact product attribute value.
*
* @return string|int
*/
public function realValue(): Attribute
{
return Attribute::make(
get: fn () => $this->product_custom_value ?: ($this->value?->value ?? '')
);
}
/**
* Attribute Value for forms
*/
public function valueForForms(): null|int|string
{
return $this->product_custom_value ?: $this->attribute_value_id;
}
/**
* Value
*/
public function value(): BelongsTo
{
return $this->belongsTo(AttributeValue::class, 'attribute_value_id');
}
}

View File

@@ -0,0 +1,65 @@
<?php
namespace App\Models\Ecommerce\Product\Property;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class ProductProperty extends Model
{
use HasFactory;
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'product_attributes';
/**
* Indicates if the model should be timestamped.
*/
public $timestamps = false;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'product_id',
'attribute_id',
];
/**
* The relations to eager load on every query.
*/
protected $with = [
'attribute',
'values',
];
/**
* Attribute
*/
public function attribute(): BelongsTo
{
return $this->belongsTo(Attribute::class, 'attribute_id');
}
/**
* Product
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class, 'product_id');
}
/**
* Property values
*/
public function values(): HasMany
{
return $this->hasMany(ProductAttributeValue::class, 'product_attribute_id');
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace App\Models\Ecommerce\Product\Review;
use App\Models\Ecommerce\Product\Product\Product;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Review extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*/
protected $fillable = [
'user_id',
'product_id',
'rating',
'title',
'content',
'is_visible',
'is_recommended',
'source',
];
/**
* Author
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Product
*/
public function product(): BelongsTo
{
return $this->belongsTo(Product::class);
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace App\Models\Legal;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\Translatable\HasTranslations;
class LegalPage extends Model
{
use HasFactory;
use HasTranslations;
/**
* The table associated with the model.
*/
protected $table = 'legal_pages';
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'slug',
'title',
'content',
'is_active',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['title', 'content'];
/**
* Retrieve the model for a bound value.
*
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
return $this->where('slug', $value)
->where('is_active', true)
->firstOrFail();
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace App\Models\Legal\User;
use App\Models\Concerns\HasSchemalessAttributes;
use App\Models\User;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\Translatable\HasTranslations;
class EntrepreneurDocs extends Model
{
use HasFactory;
use HasSchemalessAttributes;
use HasTranslations;
/**
* The table associated with the model.
*/
protected $table = 'entrepreneur_docs';
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'corporation_type',
'corporation_name',
'options',
'user_id',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['corporation_name'];
/**
* User, Document owner, Entrepreneur
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Formatted corporation type
*/
protected function formattedCorporationType(): Attribute
{
return Attribute::make(
get: fn () => match ($this->corporation_type) {
'hk' => __('Private enterprise'),
'hj' => __('Partnership enterprise'),
default => __('Entrepreneur')
}
);
}
/**
* Corporatoin name full name: "Web ulgam" HJ
*/
protected function formattedCorporationName(): Attribute
{
return Attribute::make(
get: fn () => $this->corporation_name.' '.$this->formatted_corporation_type
);
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace App\Models\Post;
use App\Models\System\Settings\Location\Province;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\Translatable\HasTranslations;
class PostBranch extends Model
{
use HasFactory;
use HasTranslations;
/**
* Table name
*/
protected $table = 'post_branches';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'province_id',
'name',
'address',
'description',
];
/**
* @var array<string>
*/
public $translatable = [
'name',
'address',
'description',
];
/**
* Related Province
*/
public function province(): BelongsTo
{
return $this->belongsTo(Province::class);
}
/**
* Scope to filter
*
* @param Builder $query
* @param string $value
* @return Builder $query
*/
public function scopeByRegion($query, $value): Builder
{
return $value
? $query->whereIn('province_id', fn ($builder) => $builder->select('id')->from('provinces')->where('region', $value))
: $query;
}
}

View File

@@ -0,0 +1,134 @@
<?php
namespace App\Models\Post\User;
use App\Models\User;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Spatie\Image\Manipulations;
use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;
use Spatie\MediaLibrary\MediaCollections\Models\Media;
class UserDoc extends Model implements HasMedia
{
use HasFactory;
use InteractsWithMedia;
/**
* Attributes that are guarded
*
* @var array
*/
protected $guarded = [];
/**
* Table
*
* @var string
*/
protected $table = 'user_docs';
/**
* The relations to eager load on every query.
*
* @var array
*/
protected $with = [
'media',
];
/**
* Media collections
*/
public function registerMediaCollections(): void
{
$this->addMediaCollection('main');
}
/**
* Media conversations
*/
public function registerMediaConversions(?Media $media = null): void
{
$this->addMediaConversion('thumb150x150')
->fit(Manipulations::FIT_CONTAIN, 150, 150);
}
/**
* User
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
/**
* Entrepreneurship types
*
* ENTREPRENEUR
*/
public const ENTREPRENEUR = 'tel';
/**
* PRIVATE ENTERPRISE
*/
public const PRIVATE_ENTERPRISE = 'hk';
/**
* PARTNERSHIP ENTERPRISE
*/
public const PARTNERSHIP_ENTERPRISE = 'hj';
/**
* Government
*/
public const GOVERNMENT = 'gov';
/**
* Closed joint stock
*/
public const CLOSED_JOINT_STOCK = 'closed_joint_stock';
/**
* Open joint stock
*/
public const OPEN_JOINT_STOCK = 'open_joint_stock';
/**
* Corparation types
*/
public static function corparationTypes(): array
{
return [
self::ENTREPRENEUR => __('Entrepreneur'),
self::PRIVATE_ENTERPRISE => __('Private enterprise'),
self::PARTNERSHIP_ENTERPRISE => __('Partnership enterprise'),
self::GOVERNMENT => __('Government company'),
self::CLOSED_JOINT_STOCK => __('Closed joint stock company'),
self::OPEN_JOINT_STOCK => __('Open joint-stock company'),
];
}
/**
* Formatted corparation type
*/
public function formattedCorparationType(): Attribute
{
return Attribute::make(
get: fn () => self::corparationTypes()[$this->corporation_type] ?? ''
);
}
/**
* Formatted corparation name
*/
public function formattedCorparationName(): Attribute
{
return Attribute::make(
get: fn () => $this->formattedCorparationType.' '.$this->corporation_name
);
}
}

View File

@@ -0,0 +1,7 @@
<?php
namespace App\Models\System\Roles;
use Spatie\Permission\Models\Permission as SpatiePermission;
class Permission extends SpatiePermission {}

View File

@@ -0,0 +1,7 @@
<?php
namespace App\Models\System\Roles;
use Spatie\Permission\Models\Role as SpatieRole;
class Role extends SpatieRole {}

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Models\System\Settings;
use Illuminate\Database\Eloquent\Model;
class Currency extends Model
{
/**
* Default currency
*
* @var string
*/
public const DEFAULT = 'TMT';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'code',
'symbol',
'format',
'exchange_rate',
];
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Models\System\Settings\Location;
use App\Models\Post\PostBranch;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Spatie\Translatable\HasTranslations;
class Province extends Model
{
use HasFactory;
use HasTranslations;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'region',
'name',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['name'];
/**
* Post branches
*/
public function postBranches(): HasMany
{
return $this->hasMany(PostBranch::class);
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace App\Models\System\Settings\Location;
class Region
{
/**
* Mary
*/
public const MR = 'mr';
/**
* Aşgabat
*/
public const AG = 'ag';
/**
* Arkadag
*/
public const AK = 'ak';
/**
* Ahal
*/
public const AH = 'ah';
/**
* Lebap
*/
public const LB = 'lb';
/**
* Balkan
*/
public const BN = 'bn';
/**
* Daşoguz
*/
public const DZ = 'dz';
/**
* Regions
*/
public static function values(): array
{
return [
self::AG => __('Ashgabat'),
self::AK => __('Arkadag'),
self::MR => __('Mary'),
self::AH => __('Ahal'),
self::LB => __('Lebap'),
self::BN => __('Balkan'),
self::DZ => __('Dashoguz'),
];
}
/**
* Default value
*/
public static function default(): string
{
return self::AG;
}
/**
* Label for given region
*/
public static function labelFor(string $region): string
{
return static::values()[$region] ?? '';
}
}

View File

@@ -0,0 +1,73 @@
<?php
namespace App\Models\System\Settings\Location;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class UserAddress extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'last_name',
'first_name',
'company_name',
'street_address',
'street_address_plus',
'zipcode',
'city',
'phone_number',
'is_default',
'type',
'user_id',
];
/**
* The dynamic attributes from mutators that should be returned with the user object.
*
* @var array
*/
protected $appends = [
'full_name',
];
/**
* Define if an address is default or not.
*/
public function isDefault(): bool
{
return $this->is_default === true;
}
/**
* User
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
/**
* Bootstrap the model and its traits.
*/
protected static function boot()
{
parent::boot();
static::creating(function ($address) {
if ($address->is_default) {
$address->user->addresses()->where('type', $address->type)->update([
'is_default' => false,
]);
}
});
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace App\Models\System\Settings;
class OS
{
/**
* MacOS
*/
public const MACOS = 'macos';
/**
* Linux
*/
public const LINUX = 'linux';
/**
* Windows
*/
public const WINDOWS = 'windows';
/**
* IOS
*/
public const IOS = 'ios';
/**
* Android
*/
public const ANDROID = 'android';
/**
* Operationg systems
*/
public static function types(): array
{
return [
self::IOS => 'IOS',
self::ANDROID => 'Android',
];
}
/**
* Website - shop.post.tm
*/
public const WEBSITE = 'site';
/**
* Admin panel - admin shop.post.tm
*/
public const ADMIN = 'admin';
/**
* Ecommerce app - postshop
*/
public const MOBILE_APP = 'mobile_app';
/**
* Seller app - postshop satyjy
*/
public const MOBILE_ADMIN = 'mobile_admin';
/**
* Our Apps
*/
public static function apps(): array
{
return [
self::WEBSITE => __('Website'),
self::ADMIN => __('Admin Panel'),
self::MOBILE_APP => __('Mobile app'),
self::MOBILE_ADMIN => __('Mobile admin'),
self::ANDROID => __('Android'),
self::IOS => __('IOS'),
];
}
/**
* Default value
*/
public static function default(): string
{
return self::WEBSITE;
}
}

View File

@@ -0,0 +1,83 @@
<?php
namespace App\Models\System\Settings\Payments;
use Illuminate\Database\Eloquent\Model;
use Spatie\Sluggable\HasSlug;
use Spatie\Sluggable\SlugOptions;
use Spatie\Translatable\HasTranslations;
class PaymentType extends Model
{
use HasSlug;
use HasTranslations;
/**
* Table
*
* @var string
*/
protected $table = 'payment_types';
/**
* Translatable fields
*
* @var array<int, string>
*/
public $translatable = [
'name',
];
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'code',
'name',
'tax',
'is_enabled',
'options',
];
/**
* Get the options for generating the slug.
*/
public function getSlugOptions(): SlugOptions
{
return SlugOptions::create()
->generateSlugsFrom('name')
->saveSlugsTo('code');
}
/**
* Create base payments
*/
public static function createBaseRecords(): void
{
collect([
[
'code' => 'cash',
'name' => ['en' => 'Cash', 'tk' => 'Nagt', 'ru' => 'Näliçni'],
'tax' => 0,
'is_enabled' => true,
'options' => null,
],
[
'code' => 'atm',
'name' => ['en' => 'ATM', 'tk' => 'Terminal', 'ru' => 'ATM'],
'tax' => 0,
'is_enabled' => true,
'options' => null,
],
[
'code' => 'online_halkbank',
'name' => ['en' => 'Online (halkbank)', 'tk' => 'Onlaýn (halkbank)', 'ru' => 'Onlaýn (halkbank)'],
'tax' => 0,
'is_enabled' => true,
'options' => null,
],
])->each(fn ($data) => static::create($data));
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Models\System\Settings;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Spatie\Translatable\HasTranslations;
class Settings extends Model
{
use HasFactory;
use HasTranslations;
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'key',
'value',
'display_name',
'locked',
];
/**
* Translatable fields
*
* @var array<string>
*/
public $translatable = ['display_name'];
/**
* Sms token
*/
public static function smsToken(): string
{
return config('sms.api.token');
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Models\System\VersionManagement;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class AppVersion extends Model
{
use HasFactory;
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'app_versions';
/**
* The attributes that are mass assignable.
*
* version - string
* os - string | in: ios, android
* important - bool
* notes - ?string
*
* @var array<string>
*/
protected $fillable = [
'version',
'os',
'important',
'notes',
];
}

203
app/Models/User.php Normal file
View File

@@ -0,0 +1,203 @@
<?php
namespace App\Models;
use App\Models\Concerns\HasEcommerceChannels;
use App\Models\Concerns\HasSchemalessAttributes;
use App\Models\Concerns\InteractsWithNova;
use App\Models\Concerns\InteractsWithRoles;
use App\Models\Ecommerce\Product\Cart\CartItem;
use App\Models\Ecommerce\Product\Favorite\Favorite;
use App\Models\Ecommerce\Product\Order\Order;
use App\Models\Ecommerce\Product\Review\Review;
use App\Models\Post\User\UserDoc;
use App\Models\System\Settings\Location\UserAddress;
use App\Repositories\System\Cache\CacheRepository;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Spatie\DeletedModels\Models\Concerns\KeepsDeletedModels;
use Spatie\Permission\Traits\HasRoles;
class User extends Authenticatable
{
use HasApiTokens;
use HasEcommerceChannels;
use HasFactory;
use HasRoles;
use HasSchemalessAttributes;
use InteractsWithNova;
use InteractsWithRoles;
use KeepsDeletedModels;
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<string>
*/
protected $fillable = [
'first_name',
'last_name',
'email',
'phone_number',
'verified',
'password',
'options',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
/**
* Get fullname of user
*
* @return string
*/
public function fullname(): Attribute
{
return Attribute::make(
get: fn () => sprintf('%s %s', $this->first_name, $this->last_name)
);
}
/**
* Get users locale
*/
public function locale(): Attribute
{
return Attribute::make(
get: fn () => $this->options->get('locale'),
);
}
/**
* Get all role names
*
* @return string
*/
public function roleNames(): Attribute
{
return Attribute::make(
get: fn () => $this->getRoleNames()->implode(', ')
);
}
/**
* Addresses
*/
public function addresses(): HasMany
{
return $this->hasMany(UserAddress::class);
}
/**
* User's carts
*/
public function carts(): HasMany
{
return $this->hasMany(CartItem::class);
}
/**
* User's favorite products
*/
public function favorites(): HasMany
{
return $this->hasMany(Favorite::class);
}
/**
* User's favorite products
*/
public function reviews(): HasMany
{
return $this->hasMany(Review::class);
}
/**
* Orders
*/
public function orders(): HasMany
{
return $this->hasMany(Order::class);
}
/**
* Documents
*/
public function documents(): HasOne
{
return $this->hasOne(UserDoc::class);
}
/**
* Get Company name
*/
public function companyName(): ?string
{
$this->loadMissing('documents');
return $this->documents?->formattedCorparationName;
}
/**
* User owns product
*/
public function ownsProduct($product): bool
{
return true;
// return CacheRepository::make();
}
/**
* Owns inventory
*/
public function ownsInventory($inventory): bool
{
return $this->channel()->id === $inventory->channel_id;
}
/**
* Check if user does not own any inventories
*/
public function doesntOwnInventory(): bool
{
return $this->inventoryCount() === 0;
}
/**
* Inventory count
*/
public function inventoryCount(): int
{
$inventoryCount = cache()->store('array')->get('user_inventory_count');
if (! $inventoryCount) {
cache()->store('array')->put('user_inventory_count', $this->channel()->inventories()->count());
}
return cache()->store('array')->get('user_inventory_count');
}
}