From d2ec26acc9d9f4993c8dd3f1aa213634c46dded5 Mon Sep 17 00:00:00 2001 From: shepard153 Date: Fri, 14 Jun 2024 17:07:16 +0200 Subject: [PATCH 1/2] WIP --- .../core/database/factories/BundleFactory.php | 30 ++++ ...2024_06_14_100000_create_bundles_table.php | 34 ++++ ..._06_14_100002_create_bundleables_table.php | 23 +++ packages/core/src/Base/Traits/HasBundles.php | 33 ++++ packages/core/src/Models/Bundle.php | 170 ++++++++++++++++++ packages/core/src/Models/ProductVariant.php | 2 + 6 files changed, 292 insertions(+) create mode 100644 packages/core/database/factories/BundleFactory.php create mode 100644 packages/core/database/migrations/2024_06_14_100000_create_bundles_table.php create mode 100644 packages/core/database/migrations/2024_06_14_100002_create_bundleables_table.php create mode 100644 packages/core/src/Base/Traits/HasBundles.php create mode 100644 packages/core/src/Models/Bundle.php diff --git a/packages/core/database/factories/BundleFactory.php b/packages/core/database/factories/BundleFactory.php new file mode 100644 index 0000000000..d859e5934f --- /dev/null +++ b/packages/core/database/factories/BundleFactory.php @@ -0,0 +1,30 @@ + TaxClass::factory()->hasTaxRateAmounts( + TaxRateAmount::factory() + ), + 'sku' => Str::random(12), + 'unit_quantity' => 1, + 'gtin' => $this->faker->unique()->isbn13, + 'mpn' => $this->faker->unique()->isbn13, + 'ean' => $this->faker->unique()->ean13, + 'shippable' => true, + ]; + } +} diff --git a/packages/core/database/migrations/2024_06_14_100000_create_bundles_table.php b/packages/core/database/migrations/2024_06_14_100000_create_bundles_table.php new file mode 100644 index 0000000000..ec62c22195 --- /dev/null +++ b/packages/core/database/migrations/2024_06_14_100000_create_bundles_table.php @@ -0,0 +1,34 @@ +prefix.'bundles', function (Blueprint $table) { + $table->id(); + $table->foreignId('tax_class_id')->constrained($this->prefix.'tax_classes'); + $table->string('tax_ref')->index()->nullable(); + $table->json('attribute_data'); + $table->integer('unit_quantity')->unsigned()->index()->default(1); + $table->string('sku')->nullable()->index(); + $table->string('gtin')->nullable()->index(); + $table->string('mpn')->nullable()->index(); + $table->string('ean')->nullable()->index(); + $table->boolean('shippable')->default(true)->index(); + $table->integer('stock')->default(0)->index(); + $table->integer('backorder')->default(0)->index(); + $table->string('purchasable')->default('always')->index(); + $table->timestamps(); + $table->softDeletes(); + }); + } + + public function down(): void + { + Schema::dropIfExists($this->prefix.'bundles'); + } +}; diff --git a/packages/core/database/migrations/2024_06_14_100002_create_bundleables_table.php b/packages/core/database/migrations/2024_06_14_100002_create_bundleables_table.php new file mode 100644 index 0000000000..1c65eb5201 --- /dev/null +++ b/packages/core/database/migrations/2024_06_14_100002_create_bundleables_table.php @@ -0,0 +1,23 @@ +prefix.'bundleables', function (Blueprint $table) { + $table->id(); + $table->foreignId('bundle_id')->constrained($this->prefix.'bundles'); + $table->morphs('bundleable'); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists($this->prefix.'bundleables'); + } +}; diff --git a/packages/core/src/Base/Traits/HasBundles.php b/packages/core/src/Base/Traits/HasBundles.php new file mode 100644 index 0000000000..e8f6df9076 --- /dev/null +++ b/packages/core/src/Base/Traits/HasBundles.php @@ -0,0 +1,33 @@ +morphToMany( + Bundle::class, + 'bundleable', + "{$prefix}bundleables", + )->withTimestamps(); + } + + + /** + * Add a bundle to the model. + */ + public function addToBundle($bundle) + { + $this->bundles()->attach($bundle); + } +} diff --git a/packages/core/src/Models/Bundle.php b/packages/core/src/Models/Bundle.php new file mode 100644 index 0000000000..8008fcd143 --- /dev/null +++ b/packages/core/src/Models/Bundle.php @@ -0,0 +1,170 @@ + 'bool', + ]; + + /** + * Return a new factory instance for the model. + */ + protected static function newFactory(): BundleFactory + { + return BundleFactory::new(); + } + + public function bundleable(): MorphTo + { + return $this->morphTo(); + } + + public function products(): MorphToMany + { + $prefix = config('lunar.database.table_prefix'); + + return $this->morphedByMany( + ProductVariant::class, + 'bundleable', + "{$prefix}bundleables" + ); + } + + /** + * Return the tax class relationship. + */ + public function taxClass(): BelongsTo + { + return $this->belongsTo(TaxClass::class); + } + + public function getPrices(): Collection + { + return $this->prices; + } + + /** + * Return the unit quantity for the variant. + */ + public function getUnitQuantity(): int + { + return $this->unit_quantity; + } + + /** + * Return the tax class. + */ + public function getTaxClass(): TaxClass + { + return Blink::once("tax_class_{$this->tax_class_id}", function () { + return $this->taxClass; + }); + } + + public function getTaxReference(): ?string + { + return $this->tax_ref; + } + + /** + * {@inheritDoc} + */ + public function getType(): string + { + return $this->shippable ? 'physical' : 'digital'; + } + + /** + * {@inheritDoc} + */ + public function isShippable(): bool + { + return $this->shippable; + } + + /** + * {@inheritDoc} + */ + public function getDescription(): string + { + return $this->product->translateAttribute('name'); + } + + /** + * {@inheritDoc} + */ + public function getOption(): void + { + return; + } + + /** + * {@inheritDoc} + */ + public function getIdentifier(): string + { + return $this->sku; + } + + public function getThumbnail(): ?Media + { + return $this->images->first(function ($media) { + return (bool) $media->pivot?->primary; + }) ?: $this->product->thumbnail; + } +} diff --git a/packages/core/src/Models/ProductVariant.php b/packages/core/src/Models/ProductVariant.php index 74b4287e53..22ef0d709b 100644 --- a/packages/core/src/Models/ProductVariant.php +++ b/packages/core/src/Models/ProductVariant.php @@ -10,6 +10,7 @@ use Lunar\Base\Casts\AsAttributeData; use Lunar\Base\Purchasable; use Lunar\Base\Traits\HasAttributes; +use Lunar\Base\Traits\HasBundles; use Lunar\Base\Traits\HasDimensions; use Lunar\Base\Traits\HasMacros; use Lunar\Base\Traits\HasPrices; @@ -53,6 +54,7 @@ class ProductVariant extends BaseModel implements Purchasable { use HasAttributes; + use HasBundles; use HasDimensions; use HasFactory; use HasMacros; From 5e6a74ed321f42a3715a0e2049a36853736bd0d9 Mon Sep 17 00:00:00 2001 From: shepard153 Date: Mon, 17 Jun 2024 09:23:29 +0200 Subject: [PATCH 2/2] WIP --- ..._103001_create_collection_bundle_table.php | 24 +++++++++++++++++++ packages/core/src/DiscountTypes/AmountOff.php | 7 ++++++ packages/core/src/Models/Bundle.php | 22 +++++++++++++---- 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 packages/core/database/migrations/2024_06_15_103001_create_collection_bundle_table.php diff --git a/packages/core/database/migrations/2024_06_15_103001_create_collection_bundle_table.php b/packages/core/database/migrations/2024_06_15_103001_create_collection_bundle_table.php new file mode 100644 index 0000000000..0e595bc4ed --- /dev/null +++ b/packages/core/database/migrations/2024_06_15_103001_create_collection_bundle_table.php @@ -0,0 +1,24 @@ +prefix.'collection_bundle', function (Blueprint $table) { + $table->id(); + $table->foreignId('collection_id')->constrained($this->prefix.'collections'); + $table->foreignId('bundle_id')->constrained($this->prefix.'bundles'); + $table->integer('position')->default(1)->index(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists($this->prefix.'collection_bundle'); + } +}; diff --git a/packages/core/src/DiscountTypes/AmountOff.php b/packages/core/src/DiscountTypes/AmountOff.php index 105bca64e9..34fc0fdd28 100644 --- a/packages/core/src/DiscountTypes/AmountOff.php +++ b/packages/core/src/DiscountTypes/AmountOff.php @@ -5,6 +5,7 @@ use Lunar\Base\ValueObjects\Cart\DiscountBreakdown; use Lunar\Base\ValueObjects\Cart\DiscountBreakdownLine; use Lunar\DataTypes\Price; +use Lunar\Models\Bundle; use Lunar\Models\Cart; use Lunar\Models\Collection; @@ -180,6 +181,12 @@ protected function getEligibleLines(Cart $cart): \Illuminate\Support\Collection if ($collectionIds->count()) { $lines = $lines->filter(function ($line) use ($collectionIds) { + if ($line->purchasable instanceof Bundle) { + return $line->purchasable->whereHas('collections', function ($query) use ($collectionIds) { + $query->whereIn((new Collection)->getTable().'.id', $collectionIds); + })->exists(); + } + return $line->purchasable->product()->whereHas('collections', function ($query) use ($collectionIds) { $query->whereIn((new Collection)->getTable().'.id', $collectionIds); })->exists(); diff --git a/packages/core/src/Models/Bundle.php b/packages/core/src/Models/Bundle.php index 8008fcd143..cd0ad79b8d 100644 --- a/packages/core/src/Models/Bundle.php +++ b/packages/core/src/Models/Bundle.php @@ -74,7 +74,19 @@ public function bundleable(): MorphTo return $this->morphTo(); } - public function products(): MorphToMany + /** + * Return the product collections relation. + */ + public function collections(): BelongsToMany + { + return $this->belongsToMany( + \Lunar\Models\Collection::class, + config('lunar.database.table_prefix').'collection_bundle' + )->withPivot(['position'])->withTimestamps(); + } + + + public function items(): MorphToMany { $prefix = config('lunar.database.table_prefix'); @@ -158,13 +170,15 @@ public function getOption(): void */ public function getIdentifier(): string { - return $this->sku; + return $this->items->map(function ($item) { + return $item->getIdentifier(); + })->implode(','); } public function getThumbnail(): ?Media { - return $this->images->first(function ($media) { + return $this->images?->first(function ($media) { return (bool) $media->pivot?->primary; - }) ?: $this->product->thumbnail; + }); } }