Bundle product data save completed

This commit is contained in:
jitendra 2019-08-21 19:24:26 +05:30
parent ec567d624f
commit 7352a97797
29 changed files with 673 additions and 112 deletions

View File

@ -248,8 +248,7 @@ Route::group(['middleware' => ['web']], function () {
'view' => 'admin::catalog.products.edit'
])->name('admin.catalog.products.productlinksearch');
//product search for linked products
Route::get('products/search/{id}', 'Webkul\Product\Http\Controllers\ProductController@searchProductForGroupedAssociation')->name('admin.catalog.products.search_product_for_grouped_association');
Route::get('products/search-simple-products', 'Webkul\Product\Http\Controllers\ProductController@searchSimpleProducts')->name('admin.catalog.products.search_simple_product');
Route::get('/products/{id}/{attribute_id}', 'Webkul\Product\Http\Controllers\ProductController@download')->defaults('_config', [
'view' => 'admin.catalog.products.edit'

View File

@ -387,7 +387,16 @@ return [
'grouped-products' => 'Grouped Products',
'search-products' => 'Search Products',
'no-result-found' => 'Products not found with same name.',
'bundle-items' => 'Bundle Items'
'bundle-items' => 'Bundle Items',
'add-option-btn-title' => 'Add Option',
'option-title' => 'Option Title',
'input-type' => 'Input Type',
'is-required' => 'Is Required',
'select' => 'Select',
'radio' => 'Radio',
'checkbox' => 'Checkbox',
'multiselect' => 'Multiselect',
'new-option' => 'New Option'
],
'attributes' => [
'title' => 'الصفات',

View File

@ -417,7 +417,16 @@ return [
'search-products' => 'Search Products',
'no-result-found' => 'Products not found with same name.',
'channel' => 'Channels',
'bundle-items' => 'Bundle Items'
'bundle-items' => 'Bundle Items',
'add-option-btn-title' => 'Add Option',
'option-title' => 'Option Title',
'input-type' => 'Input Type',
'is-required' => 'Is Required',
'select' => 'Select',
'radio' => 'Radio',
'checkbox' => 'Checkbox',
'multiselect' => 'Multiselect',
'new-option' => 'New Option'
],
'attributes' => [

View File

@ -399,7 +399,16 @@ return [
'product-search-hint' => 'شروع به تایپ نام محصول کنید',
'no-result-found' => 'محصولاتی با همین نام یافت نشد',
'searching' => 'جست و جو ...',
'bundle-items' => 'Bundle Items'
'bundle-items' => 'Bundle Items',
'add-option-btn-title' => 'Add Option',
'option-title' => 'Option Title',
'input-type' => 'Input Type',
'is-required' => 'Is Required',
'select' => 'Select',
'radio' => 'Radio',
'checkbox' => 'Checkbox',
'multiselect' => 'Multiselect',
'new-option' => 'New Option'
],
'attributes' => [

View File

@ -393,7 +393,16 @@ return [
'grouped-products' => 'Grouped Products',
'search-products' => 'Search Products',
'no-result-found' => 'Products not found with same name.',
'bundle-items' => 'Bundle Items'
'bundle-items' => 'Bundle Items',
'add-option-btn-title' => 'Add Option',
'option-title' => 'Option Title',
'input-type' => 'Input Type',
'is-required' => 'Is Required',
'select' => 'Select',
'radio' => 'Radio',
'checkbox' => 'Checkbox',
'multiselect' => 'Multiselect',
'new-option' => 'New Option'
],
'attributes' => [
'title' => 'Atributos',

View File

@ -5,7 +5,344 @@
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.bundle.controls.before', ['product' => $product]) !!}
<bundle-option-list></bundle-option-list>
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.bundle.controls.after', ['product' => $product]) !!}
</div>
</accordian>
</accordian>
@push('scripts')
@parent
<script type="text/x-template" id="bundle-option-list-template">
<div class="">
<button type="button" class="btn btn-md btn-primary" @click="addOption" style="margin-bottom: 20px;">
{{ __('admin::app.catalog.products.add-option-btn-title') }}
</button>
<bundle-option-item
v-for='(option, index) in options'
:option="option"
:key="index"
:index="index"
@onRemoveOption="removeOption($event)"
></bundle-option-item>
</div>
</script>
<script type="text/x-template" id="bundle-option-item-template">
<accordian :active="true">
<div slot="header">
<i class="icon expand-icon left"></i>
<h1 v-if="option.label">@{{ option.label }}</h1>
<h1 v-else>{{ __('admin::app.catalog.products.new-option') }}</h1>
<i class="icon trash-icon" @click="removeOption()"></i>
</div>
<div slot="body">
<div class="control-group" :class="[errors.has(titleInputName + '[label]') ? 'has-error' : '']">
<label class="required">{{ __('admin::app.catalog.products.option-title') }}</label>
<input type="text" v-validate="'required'" :name="titleInputName + '[label]'" v-model="option.label" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.option-title') }}&quot;"/>
<span class="control-error" v-if="errors.has(titleInputName + '[label]')">@{{ errors.first(titleInputName + '[label]') }}</span>
</div>
<div class="control-group" :class="[errors.has(inputName + '[type]') ? 'has-error' : '']">
<label class="required">{{ __('admin::app.catalog.products.input-type') }}</label>
<select v-validate="'required'" :name="inputName + '[type]'" v-model="option.type" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.input-type') }}&quot;">
<option value="select">{{ __('admin::app.catalog.products.select') }}</option>
<option value="radio">{{ __('admin::app.catalog.products.radio') }}</option>
<option value="checkbox">{{ __('admin::app.catalog.products.checkbox') }}</option>
<option value="multiselect">{{ __('admin::app.catalog.products.multiselect') }}</option>
</select>
<span class="control-error" v-if="errors.has(inputName + '[type]')">@{{ errors.first(inputName + '[type]') }}</span>
</div>
<div class="control-group" :class="[errors.has(inputName + '[is_required]') ? 'has-error' : '']">
<label class="required">{{ __('admin::app.catalog.products.is-required') }}</label>
<select v-validate="'required'" :name="inputName + '[is_required]'" v-model="option.is_required" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.is-required') }}&quot;">
<option value="1">{{ __('admin::app.catalog.products.yes') }}</option>
<option value="0">{{ __('admin::app.catalog.products.no') }}</option>
</select>
<span class="control-error" v-if="errors.has(inputName + '[is_required]')">@{{ errors.first(inputName + '[is_required]') }}</span>
</div>
<div class="control-group" :class="[errors.has(inputName + '[sort_order]') ? 'has-error' : '']">
<label class="required">{{ __('admin::app.catalog.products.sort-order') }}</label>
<input type="text" v-validate="'required'" :name="inputName + '[sort_order]'" v-model="option.sort_order" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.sort-order') }}&quot;"/>
<span class="control-error" v-if="errors.has(inputName + '[sort_order]')">@{{ errors.first(inputName + '[sort_order]') }}</span>
</div>
<div class="section">
<div class="secton-title">
<span>Products</span>
</div>
<div class="section-content">
<bundle-product-list
:bundle-option-products="option.bundle_option_products"
:control-name="inputName">
</bundle-product-list>
</div>
</div>
</div>
</accordian>
</script>
<script type="text/x-template" id="bundle-product-list-template">
<div>
<div class="control-group">
<label>{{ __('admin::app.catalog.products.search-products') }}</label>
<input type="text" class="control" placeholder="{{ __('admin::app.catalog.products.product-search-hint') }}" v-model.lazy="search_term" v-debounce="500" autocomplete="off">
<div class="linked-product-search-result">
<ul>
<li v-for='(product, index) in searched_results' v-if='searched_results.length' @click="addProduct(product)">
@{{ product.name }}
</li>
<li v-if='! searched_results.length && search_term.length && ! is_searching'>
{{ __('admin::app.catalog.products.no-result-found') }}
</li>
<li v-if="is_searching && search_term.length">
{{ __('admin::app.catalog.products.searching') }}
</li>
</ul>
</div>
</div>
<div class="table" style="margin-top: 20px; overflow-x: unset;">
<table>
<thead>
<tr>
<th class="name">{{ __('admin::app.catalog.products.name') }}</th>
<th class="sku">{{ __('admin::app.catalog.products.sku') }}</th>
<th class="qty">{{ __('admin::app.catalog.products.qty') }}</th>
<th class="sort-order">{{ __('admin::app.catalog.products.sort-order') }}</th>
<th class="actions"></th>
</tr>
</thead>
<tbody>
<bundle-product-item
v-for='(product, index) in bundle_option_products'
:product="product"
:key="index"
:index="index"
:control-name="controlName"
@onRemoveProduct="removeProduct($event)">
</bundle-product-item>
</tbody>
</table>
</div>
</div>
</script>
<script type="text/x-template" id="bundle-product-item-template">
<tr>
<td>
@{{ product.product.name }}
<input type="hidden" :name="[inputName + '[product_id]']" :value="product.product.id"/>
</td>
<td>@{{ product.product.sku }}</td>
<td>
<div class="control-group" :class="[errors.has(inputName + '[qty]') ? 'has-error' : '']">
<input type="number" v-validate="'required|min_value:0'" :name="[inputName + '[qty]']" v-model="product.qty" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.qty') }}&quot;"/>
<span class="control-error" v-if="errors.has(inputName + '[qty]')">@{{ errors.first(inputName + '[qty]') }}</span>
</div>
</td>
<td>
<div class="control-group" :class="[errors.has(inputName + '[sort_order]') ? 'has-error' : '']">
<input type="number" v-validate="'required|min_value:0'" :name="[inputName + '[sort_order]']" v-model="product.sort_order" class="control" data-vv-as="&quot;{{ __('admin::app.catalog.products.sort-order') }}&quot;"/>
<span class="control-error" v-if="errors.has(inputName + '[sort_order]')">@{{ errors.first(inputName + '[sort_order]') }}</span>
</div>
</td>
<td class="actions">
<i class="icon remove-icon" @click="removeProduct()"></i>
</td>
</tr>
</script>
<script>
Vue.component('bundle-option-list', {
template: '#bundle-option-list-template',
inject: ['$validator'],
data: function() {
return {
options: @json($product->bundle_options()->with(['product', 'bundle_option_products', 'bundle_option_products.product'])->get())
}
},
methods: {
addOption: function() {
this.options.push({
label: '',
type: 'select',
is_required: 1,
sort_order: 0,
bundle_option_products: []
});
},
removeOption: function(option) {
let index = this.options.indexOf(option)
this.options.splice(index, 1)
},
}
});
Vue.component('bundle-option-item', {
template: '#bundle-option-item-template',
props: ['index', 'option'],
inject: ['$validator'],
data: function() {
return {
}
},
computed: {
titleInputName: function () {
if (this.option.id)
return "bundle_options[" + this.option.id + "]" + '[{{$locale}}]';
return "bundle_options[option_" + this.index + "]" + '[{{$locale}}]';
},
inputName: function () {
if (this.option.id)
return "bundle_options[" + this.option.id + "]";
return "bundle_options[option_" + this.index + "]";
}
},
methods: {
removeOption: function() {
this.$emit('onRemoveOption', this.option)
}
}
});
Vue.component('bundle-product-list', {
template: '#bundle-product-list-template',
inject: ['$validator'],
props: ['controlName', 'bundleOptionProducts'],
data: function() {
return {
search_term: '',
is_searching: false,
searched_results: [],
bundle_option_products: this.bundleOptionProducts
}
},
watch: {
'search_term': function(newVal, oldVal) {
this.search('products')
}
},
methods: {
addProduct: function(item, key) {
this.bundle_option_products.push({
product: item,
qty: 0,
sort_order: 0
});
this.search_term = '';
this.searched_result = [];
},
removeProduct: function(product) {
let index = this.bundle_option_products.indexOf(product)
this.bundle_option_products.splice(index, 1)
},
search: function (key) {
this.is_searching = true;
if (this.search_term.length < 3) {
this.searched_results = [];
this.is_searching = false;
return;
}
var this_this = this;
this.$http.get ("{{ route('admin.catalog.products.search_simple_product') }}", {params: {query: this.search_term}})
.then (function(response) {
this_this.searched_results = response.data;
this_this.is_searching = false;
})
.catch (function (error) {
this_this.is_searching = false;
})
}
}
});
Vue.component('bundle-product-item', {
template: '#bundle-product-item-template',
props: ['controlName', 'index', 'product'],
inject: ['$validator'],
computed: {
inputName: function () {
if (this.product.id)
return this.controlName + "[products][" + this.product.id + "]";
return this.controlName + "[products][product_" + this.index + "]";
}
},
methods: {
removeProduct: function () {
this.$emit('onRemoveProduct', this.product)
}
}
});
</script>
@endpush

View File

@ -2,7 +2,7 @@
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.categories.before', ['product' => $product]) !!}
<accordian :title="'{{ __('admin::app.catalog.products.categories') }}'" :active="true">
<accordian :title="'{{ __('admin::app.catalog.products.categories') }}'" :active="false">
<div slot="body">
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.categories.controls.before', ['product' => $product]) !!}

View File

@ -6,7 +6,7 @@
])->pluck('channel')->unique()->toArray();
?>
<accordian :title="'{{ __('admin::app.catalog.products.channel') }}'" :active="true">
<accordian :title="'{{ __('admin::app.catalog.products.channel') }}'" :active="false">
<div slot="body">
<div class="control-group" :class="[errors.has('channels[]') ? 'has-error' : '']">
<label for="channels" class="required">{{ __('admin::app.catalog.products.channel') }}</label>

View File

@ -169,7 +169,7 @@
var this_this = this;
this.$http.get ("{{ route('admin.catalog.products.search_product_for_grouped_association', $product->id) }}", {params: {query: this.search_term}})
this.$http.get ("{{ route('admin.catalog.products.search_simple_product') }}", {params: {query: this.search_term}})
.then (function(response) {
this_this.searched_results = response.data;

View File

@ -1,6 +1,6 @@
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.images.before', ['product' => $product]) !!}
<accordian :title="'{{ __('admin::app.catalog.products.images') }}'" :active="true">
<accordian :title="'{{ __('admin::app.catalog.products.images') }}'" :active="false">
<div slot="body">
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.images.controls.before', ['product' => $product]) !!}

View File

@ -1,6 +1,6 @@
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.inventories.before', ['product' => $product]) !!}
<accordian :title="'{{ __('admin::app.catalog.products.inventories') }}'" :active="true">
<accordian :title="'{{ __('admin::app.catalog.products.inventories') }}'" :active="false">
<div slot="body">
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.inventories.controls.before', ['product' => $product]) !!}

View File

@ -1,6 +1,6 @@
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.product_links.before', ['product' => $product]) !!}
<accordian :title="'{{ __('admin::app.catalog.products.product-link') }}'" :active="true">
<accordian :title="'{{ __('admin::app.catalog.products.product-link') }}'" :active="false">
<div slot="body">
<linked-products></linked-products>

View File

@ -59,14 +59,14 @@
<input name="_method" type="hidden" value="PUT">
@foreach ($product->attribute_family->attribute_groups as $attributeGroup)
@foreach ($product->attribute_family->attribute_groups as $index => $attributeGroup)
<?php $customAttributes = $product->getEditableAttributes($attributeGroup); ?>
@if (count($customAttributes))
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.' . $attributeGroup->name . '.before', ['product' => $product]) !!}
<accordian :title="'{{ __($attributeGroup->name) }}'" :active="true">
<accordian :title="'{{ __($attributeGroup->name) }}'" :active="{{$index == 0 ? 'true' : 'false'}}">
<div slot="body">
{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.' . $attributeGroup->name . '.controls.before', ['product' => $product]) !!}

View File

@ -2,6 +2,6 @@
namespace Webkul\Product\Contracts;
interface ProductBundleProduct
interface ProductBundleOptionProduct
{
}

View File

@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductBundleProductsTable extends Migration
class CreateProductBundleOptionProductsTable extends Migration
{
/**
* Run the migrations.
@ -13,9 +13,10 @@ class CreateProductBundleProductsTable extends Migration
*/
public function up()
{
Schema::create('product_bundle_products', function (Blueprint $table) {
Schema::create('product_bundle_option_products', function (Blueprint $table) {
$table->increments('id');
$table->integer('qty')->default(0);
$table->boolean('is_default')->default(0);
$table->integer('sort_order')->default(0);
$table->integer('product_bundle_option_id')->unsigned();
@ -33,6 +34,6 @@ class CreateProductBundleProductsTable extends Migration
*/
public function down()
{
Schema::dropIfExists('product_bundle_products');
Schema::dropIfExists('product_bundle_option_products');
}
}

View File

@ -8,7 +8,6 @@ use Webkul\Category\Repositories\CategoryRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductDownloadableLinkRepository;
use Webkul\Product\Repositories\ProductDownloadableSampleRepository;
use Webkul\Product\Repositories\ProductGroupedProductRepository;
use Webkul\Attribute\Repositories\AttributeFamilyRepository;
use Webkul\Inventory\Repositories\InventorySourceRepository;
use Illuminate\Support\Facades\Storage;
@ -70,13 +69,6 @@ class ProductController extends Controller
*/
protected $inventorySourceRepository;
/**
* ProductGroupedProductRepository object
*
* @var Object
*/
protected $productGroupedProductRepository;
/**
* Create a new controller instance.
*
@ -86,7 +78,6 @@ class ProductController extends Controller
* @param \Webkul\Product\Repositories\ProductDownloadableSampleRepository $productDownloadableSampleRepository
* @param \Webkul\Attribute\Repositories\AttributeFamilyRepository $attributeFamilyRepository
* @param \Webkul\Inventory\Repositories\InventorySourceRepository $inventorySource
* @param \Webkul\Inventory\Repositories\ProductGroupedProductRepository $productGroupedProductRepository
* @return void
*/
public function __construct(
@ -95,8 +86,7 @@ class ProductController extends Controller
ProductDownloadableLinkRepository $productDownloadableLinkRepository,
ProductDownloadableSampleRepository $productDownloadableSampleRepository,
AttributeFamilyRepository $attributeFamilyRepository,
InventorySourceRepository $inventorySourceRepository,
ProductGroupedProductRepository $productGroupedProductRepository
InventorySourceRepository $inventorySourceRepository
)
{
$this->_config = request('_config');
@ -112,8 +102,6 @@ class ProductController extends Controller
$this->attributeFamilyRepository = $attributeFamilyRepository;
$this->inventorySourceRepository = $inventorySourceRepository;
$this->productGroupedProductRepository = $productGroupedProductRepository;
}
/**
@ -367,14 +355,14 @@ class ProductController extends Controller
}
/**
* Search simple products for grouped product association
* Search simple products
*
* @return \Illuminate\Http\JsonResponse
*/
public function searchProductForGroupedAssociation()
public function searchSimpleProducts()
{
return response()->json(
$this->productGroupedProductRepository->searchSimpleProducts(request('id'), request()->input('query'))
$this->productRepository->searchSimpleProducts(request()->input('query'))
);
}
}

View File

@ -165,6 +165,14 @@ class Product extends Model implements ProductContract
return $this->hasMany(ProductGroupedProductProxy::modelClass());
}
/**
* Get the bundle options that owns the product.
*/
public function bundle_options()
{
return $this->hasMany(ProductBundleOptionProxy::modelClass());
}
/**
* @param integer $qty
*

View File

@ -20,4 +20,12 @@ class ProductBundleOption extends TranslatableModel implements ProductBundleOpti
{
return $this->belongsTo(ProductProxy::modelClass());
}
/**
* Get the bundle option products that owns the bundle option.
*/
public function bundle_option_products()
{
return $this->hasMany(ProductBundleOptionProductProxy::modelClass());
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Product\Contracts\ProductBundleOptionProduct as ProductBundleOptionProductContract;
class ProductBundleOptionProduct extends Model implements ProductBundleOptionProductContract
{
public $timestamps = false;
protected $fillable = ['qty', 'sort_order', 'is_default', 'product_bundle_option_id', 'product_id'];
/**
* Get the product that owns the image.
*/
public function product()
{
return $this->belongsTo(ProductProxy::modelClass());
}
}

View File

@ -4,7 +4,7 @@ namespace Webkul\Product\Models;
use Konekt\Concord\Proxies\ModelProxy;
class ProductBundleProductProxy extends ModelProxy
class ProductBundleOptionProductProxy extends ModelProxy
{
}

View File

@ -1,21 +0,0 @@
<?php
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Product\Contracts\ProductBundleProduct as ProductBundleProductContract;
class ProductBundleProduct extends Model implements ProductBundleProductContract
{
public $timestamps = false;
protected $fillable = ['qty', 'sort_order', 'product_bundle_option_id', 'product_id'];
/**
* Get the product that owns the image.
*/
public function product()
{
return $this->belongsTo(ProductProxy::modelClass());
}
}

View File

@ -20,6 +20,6 @@ class ModuleServiceProvider extends BaseModuleServiceProvider
\Webkul\Product\Models\ProductGroupedProduct::class,
\Webkul\Product\Models\ProductBundleOption::class,
\Webkul\Product\Models\ProductBundleOptionTranslation::class,
\Webkul\Product\Models\ProductBundleProduct::class,
\Webkul\Product\Models\ProductBundleOptionProduct::class,
];
}

View File

@ -0,0 +1,48 @@
<?php
namespace Webkul\Product\Repositories;
use Webkul\Core\Eloquent\Repository;
/**
* ProductBundleOptionProduct Repository
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ProductBundleOptionProductRepository extends Repository
{
public function model()
{
return 'Webkul\Product\Contracts\ProductBundleOptionProduct';
}
/**
* @param array $data
* @param ProductBundleOption $productBundleOption
* @return void
*/
public function saveBundleOptonProducts($data, $productBundleOption)
{
$previousBundleOptionProductIds = $productBundleOption->bundle_option_products()->pluck('id');
if (isset($data['products'])) {
foreach ($data['products'] as $bundleOptionProductId => $bundleOptionProductInputs) {
if (str_contains($bundleOptionProductId, 'product_')) {
$this->create(array_merge([
'product_bundle_option_id' => $productBundleOption->id,
], $bundleOptionProductInputs));
} else {
if (is_numeric($index = $previousBundleOptionProductIds->search($bundleOptionProductId)))
$previousBundleOptionProductIds->forget($index);
$this->update($bundleOptionProductInputs, $bundleOptionProductId);
}
}
}
foreach ($previousBundleOptionProductIds as $previousBundleOptionProductId) {
$this->delete($previousBundleOptionProductId);
}
}
}

View File

@ -2,6 +2,7 @@
namespace Webkul\Product\Repositories;
use Illuminate\Container\Container as App;
use Webkul\Core\Eloquent\Repository;
/**
@ -12,8 +13,64 @@ use Webkul\Core\Eloquent\Repository;
*/
class ProductBundleOptionRepository extends Repository
{
/**
* ProductBundleOptionProductRepository object
*
* @var ProductBundleOptionProductRepository
*/
protected $productBundleOptionProductRepository;
/**
* Create a new controller instance.
*
* @param Webkul\Product\Repositories\ProductBundleOptionProductRepository $productBundleOptionProductRepository
* @return void
*/
public function __construct(
ProductBundleOptionProductRepository $productBundleOptionProductRepository,
App $app
)
{
$this->productBundleOptionProductRepository = $productBundleOptionProductRepository;
parent::__construct($app);
}
public function model()
{
return 'Webkul\Product\Contracts\ProductBundleOption';
}
/**
* @param array $data
* @param Product $product
* @return void
*/
public function saveBundleOptons($data, $product)
{
$previousBundleOptionIds = $product->bundle_options()->pluck('id');
if (isset($data['bundle_options'])) {
foreach ($data['bundle_options'] as $bundleOptionId => $bundleOptionInputs) {
if (str_contains($bundleOptionId, 'option_')) {
$productBundleOption = $this->create(array_merge([
'product_id' => $product->id,
], $bundleOptionInputs));
} else {
$productBundleOption = $this->find($bundleOptionId);
if (is_numeric($index = $previousBundleOptionIds->search($bundleOptionId)))
$previousBundleOptionIds->forget($index);
$this->update($bundleOptionInputs, $bundleOptionId);
}
$this->productBundleOptionProductRepository->saveBundleOptonProducts($bundleOptionInputs, $productBundleOption);
}
}
foreach ($previousBundleOptionIds as $previousBundleOptionId) {
$this->delete($previousBundleOptionId);
}
}
}

View File

@ -1,19 +0,0 @@
<?php
namespace Webkul\Product\Repositories;
use Webkul\Core\Eloquent\Repository;
/**
* ProductBundleProduct Repository
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ProductBundleProductRepository extends Repository
{
public function model()
{
return 'Webkul\Product\Contracts\ProductBundleProduct';
}
}

View File

@ -4,7 +4,6 @@ namespace Webkul\Product\Repositories;
use Webkul\Core\Eloquent\Repository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductFlatRepository;
/**
* Product Grouped Product Repository
@ -19,39 +18,6 @@ class ProductGroupedProductRepository extends Repository
return 'Webkul\Product\Contracts\ProductGroupedProduct';
}
/**
* Search simple products for grouped product association
*
* @param integer $productId
* @param string $term
* @return \Illuminate\Support\Collection
*/
public function searchSimpleProducts($productId, $term)
{
$product = app(ProductRepository::class)->find($productId);
$groupedProductIds = $product->grouped_products()->pluck('product_id');
$groupedProductIds = $groupedProductIds->concat([$productId]);
return app(ProductFlatRepository::class)->scopeQuery(function($query) use($groupedProductIds, $term) {
$channel = request()->get('channel') ?: (core()->getCurrentChannelCode() ?: core()->getDefaultChannelCode());
$locale = request()->get('locale') ?: app()->getLocale();
return $query->distinct()
->addSelect('product_flat.*')
->addSelect('product_flat.product_id as id')
->leftJoin('products', 'product_flat.product_id', '=', 'products.id')
->where('products.type', 'simple')
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->whereNotIn('product_flat.product_id', $groupedProductIds)
->where('product_flat.name', 'like', '%' . urldecode($term) . '%')
->orderBy('product_id', 'desc');
})->get();
}
/**
* @param array $data
* @param Product $product

View File

@ -8,6 +8,7 @@ use Illuminate\Support\Facades\Event;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Core\Eloquent\Repository;
use Webkul\Product\Repositories\ProductFlatRepository;
/**
* Product Repository
@ -328,4 +329,29 @@ class ProductRepository extends Repository
return $superAttrbutes;
}
/**
* Search simple products for grouped product association
*
* @param string $term
* @return \Illuminate\Support\Collection
*/
public function searchSimpleProducts($term)
{
return app(ProductFlatRepository::class)->scopeQuery(function($query) use($term) {
$channel = request()->get('channel') ?: (core()->getCurrentChannelCode() ?: core()->getDefaultChannelCode());
$locale = request()->get('locale') ?: app()->getLocale();
return $query->distinct()
->addSelect('product_flat.*')
->addSelect('product_flat.product_id as id')
->leftJoin('products', 'product_flat.product_id', '=', 'products.id')
->where('products.type', 'simple')
->where('product_flat.channel', $channel)
->where('product_flat.locale', $locale)
->where('product_flat.name', 'like', '%' . urldecode($term) . '%')
->orderBy('product_id', 'desc');
})->get();
}
}

View File

@ -2,6 +2,14 @@
namespace Webkul\Product\Type;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Product\Repositories\ProductRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductBundleOptionRepository;
use Webkul\Product\Helpers\ProductImage;
/**
* Class Bundle.
*
@ -10,6 +18,55 @@ namespace Webkul\Product\Type;
*/
class Bundle extends AbstractType
{
/**
* AttributeRepository instance
*
* @var AttributeRepository
*/
protected $attributeRepository;
/**
* ProductRepository instance
*
* @var ProductRepository
*/
protected $productRepository;
/**
* ProductAttributeValueRepository instance
*
* @var ProductAttributeValueRepository
*/
protected $attributeValueRepository;
/**
* ProductInventoryRepository instance
*
* @var ProductInventoryRepository
*/
protected $productInventoryRepository;
/**
* ProductImageRepository instance
*
* @var ProductImageRepository
*/
protected $productImageRepository;
/**
* ProductBundleOptionRepository instance
*
* @var ProductBundleOptionRepository
*/
protected $productBundleOptionRepository;
/**
* Product Image helper instance
*
* @var ProductImage
*/
protected $productImageHelper;
/**
* Skip attribute for Bundle product type
*
@ -28,4 +85,54 @@ class Bundle extends AbstractType
'admin::catalog.products.accordians.bundle-items',
'admin::catalog.products.accordians.product-links'
];
/**
* Create a new product type instance.
*
* @param Webkul\Attribute\Repositories\AttributeRepository $attributeRepository
* @param Webkul\Product\Repositories\ProductRepository $productRepository
* @param Webkul\Product\Repositories\ProductAttributeValueRepository $attributeValueRepository
* @param Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
* @param Webkul\Product\Repositories\ProductImageRepository $productImageRepository
* @param Webkul\Product\Repositories\ProductBundleOptionRepository $productBundleOptionRepository
* @param Webkul\Product\Helpers\ProductImage $productImageHelper
* @return void
*/
public function __construct(
AttributeRepository $attributeRepository,
ProductRepository $productRepository,
ProductAttributeValueRepository $attributeValueRepository,
ProductInventoryRepository $productInventoryRepository,
productImageRepository $productImageRepository,
ProductBundleOptionRepository $productBundleOptionRepository,
ProductImage $productImageHelper
)
{
parent::__construct(
$attributeRepository,
$productRepository,
$attributeValueRepository,
$productInventoryRepository,
$productImageRepository,
$productImageHelper
);
$this->productBundleOptionRepository = $productBundleOptionRepository;
}
/**
* @param array $data
* @param $id
* @param string $attribute
* @return Product
*/
public function update(array $data, $id, $attribute = "id")
{
$product = parent::update($data, $id, $attribute);
if (request()->route()->getName() != 'admin.catalog.products.massupdate')
$this->productBundleOptionRepository->saveBundleOptons($data, $product);
return $product;
}
}

View File

@ -9,7 +9,6 @@ use Webkul\Product\Repositories\ProductInventoryRepository;
use Webkul\Product\Repositories\ProductImageRepository;
use Webkul\Product\Repositories\ProductDownloadableLinkRepository;
use Webkul\Product\Repositories\ProductDownloadableSampleRepository;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Product\Helpers\ProductImage;
use Webkul\Checkout\Models\CartItem;