Merge pull request #14 from jitendra-webkul/jitendra

Product edit page added with all attribuite type fields
This commit is contained in:
JItendra Singh 2018-08-02 09:51:00 +05:30 committed by GitHub
commit ba7348e204
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 447 additions and 76 deletions

View File

@ -4,6 +4,10 @@ window.VeeValidate = require("vee-validate");
Vue.use(VeeValidate);
Vue.component("datetime", require("./components/datetime"));
Vue.component("date", require("./components/date"));
require('vue-flatpickr/theme/airbnb.css');
$(document).ready(function () {
Vue.config.ignoredElements = [
'option-wrapper',

View File

@ -0,0 +1,26 @@
<template>
<Flatpickr class="control" :options="fpOptions"/>
</template>
<script>
import VueFlatpickr from "vue-flatpickr";
Vue.use(VueFlatpickr);
export default {
props: {
default: String
},
data() {
const now = new Date(this.default);
return {
fpOptions: {
utc: false,
enableTime: false
}
};
}
};
</script>

View File

@ -1,38 +1,27 @@
<template>
<Flatpickr class="datetime-field" :options="fpOptions" />
<Flatpickr class="control" :options="fpOptions"/>
</template>
<script>
window.Vue = require("vue");
import VueFlatpickr from "vue-flatpickr";
import "vue-flatpickr/theme/airbnb.css";
import VueFlatpickr from "vue-flatpickr";
Vue.use(VueFlatpickr);
Vue.use(VueFlatpickr);
export default {
data() {
const now = new Date();
console.log(now);
return {
fpOptions: {
utc: false,
defaultDate: now,
enableTime: true
}
};
},
export default {
props: {
default: String
},
mounted: function() {
console.log("Date time compoenent mounted");
}
};
</script>
<style>
.datetime-field {
border: 2px solid #c7c7c7;
border-radius: 3px;
font-family: "Montserrat", sans-serif;
height: 35px;
padding-left: 5px;
color: gray;
}
</style>
data() {
const now = new Date(this.default);
return {
fpOptions: {
utc: false,
defaultDate: now,
enableTime: true
}
};
}
};
</script>

View File

@ -77,7 +77,9 @@ return [
'sku' => 'SKU',
'configurable-attributes' => 'Configurable Attributes',
'attribute-header' => 'Attribute(s)',
'attribute-option-header' => 'Attribute Option(s)'
'attribute-option-header' => 'Attribute Option(s)',
'no' => 'No',
'yes' => 'Yes'
],
'attributes' => [
'add-title' => 'Add Attribute',
@ -92,7 +94,6 @@ return [
'boolean' => 'Boolean',
'select' => 'Select',
'multiselect' => 'Multiselect',
'checkbox' => 'Checkbox',
'datetime' => 'Datetime',
'date' => 'Date',
'label' => 'Label',
@ -116,7 +117,8 @@ return [
'value_per_channel' => 'Value Per Channel',
'value_per_channel' => 'Value Per Channel',
'is_filterable' => 'Use in Layered Navigation',
'is_configurable' => 'Use To Create Configurable Product'
'is_configurable' => 'Use To Create Configurable Product',
'admin_name' => 'Admin Name'
],
'families' => [
'families' => 'Families',

View File

@ -42,7 +42,6 @@
<option value="boolean">{{ __('admin::app.catalog.attributes.boolean') }}</option>
<option value="select">{{ __('admin::app.catalog.attributes.select') }}</option>
<option value="multiselect">{{ __('admin::app.catalog.attributes.multiselect') }}</option>
<option value="checkbox">{{ __('admin::app.catalog.attributes.checkbox') }}</option>
<option value="datetime">{{ __('admin::app.catalog.attributes.datetime') }}</option>
<option value="date">{{ __('admin::app.catalog.attributes.date') }}</option>
</select>
@ -104,8 +103,7 @@
<label for="validation">{{ __('admin::app.catalog.attributes.input_validation') }}</label>
<select class="control" id="validation" name="validation">
<option value=""></option>
<option value="number">{{ __('admin::app.catalog.attributes.number') }}</option>
<option value="decimal">{{ __('admin::app.catalog.attributes.decimal') }}</option>
<option value="numeric">{{ __('admin::app.catalog.attributes.number') }}</option>
<option value="email">{{ __('admin::app.catalog.attributes.email') }}</option>
<option value="url">{{ __('admin::app.catalog.attributes.url') }}</option>
</select>
@ -173,6 +171,8 @@
<table>
<thead>
<tr>
<th>{{ __('admin::app.catalog.attributes.admin_name') }}</th>
@foreach(Webkul\Core\Models\Locale::all() as $locale)
<th>{{ $locale->name . ' (' . $locale->code . ')' }}</th>
@ -187,6 +187,13 @@
<tbody>
<tr v-for="row in optionRows">
<td>
<div class="control-group" :class="[errors.has(adminName(row)) ? 'has-error' : '']">
<input type="text" v-validate="'required'" v-model="row['admin_name']" :name="adminName(row)" class="control"/>
<span class="control-error" v-if="errors.has(adminName(row))">@{{ errors.first(adminName(row)) }}</span>
</div>
</td>
@foreach(Webkul\Core\Models\Locale::all() as $locale)
<td>
<div class="control-group" :class="[errors.has(localeInputName(row, '{{ $locale->code }}')) ? 'has-error' : '']">
@ -253,6 +260,10 @@
Vue.delete(this.optionRows, index);
},
adminName (row) {
return 'options[' + row.id + '][admin_name]';
},
localeInputName (row, locale) {
return 'options[' + row.id + '][' + locale + '][label]';
},

View File

@ -57,9 +57,6 @@
<option value="multiselect" {{ $selectedOption == 'multiselect' ? 'selected' : '' }}>
{{ __('admin::app.catalog.attributes.multiselect') }}
</option>
<option value="checkbox" {{ $selectedOption == 'checkbox' ? 'selected' : '' }}>
{{ __('admin::app.catalog.attributes.checkbox') }}
</option>
<option value="datetime" {{ $selectedOption == 'datetime' ? 'selected' : '' }}>
{{ __('admin::app.catalog.attributes.datetime') }}
</option>
@ -231,6 +228,8 @@
<table>
<thead>
<tr>
<th>{{ __('admin::app.catalog.attributes.admin_name') }}</th>
@foreach(Webkul\Core\Models\Locale::all() as $locale)
<th>{{ $locale->name . ' (' . $locale->code . ')' }}</th>
@ -245,6 +244,13 @@
<tbody>
<tr v-for="row in optionRows">
<td>
<div class="control-group" :class="[errors.has(adminName(row)) ? 'has-error' : '']">
<input type="text" v-validate="'required'" v-model="row['admin_name']" :name="adminName(row)" class="control"/>
<span class="control-error" v-if="errors.has(adminName(row))">@{{ errors.first(adminName(row)) }}</span>
</div>
</td>
@foreach(Webkul\Core\Models\Locale::all() as $locale)
<td>
<div class="control-group" :class="[errors.has(localeInputName(row, '{{ $locale->code }}')) ? 'has-error' : '']">
@ -292,7 +298,7 @@
created () {
@foreach($attribute->options as $option)
this.optionRowCount++;
var row = {'id': '{{ $option->id }}', 'sort_order': '{{ $option->sort_order }}'};
var row = {'id': '{{ $option->id }}', 'admin_name': '{{ $option->admin_name }}', 'sort_order': '{{ $option->sort_order }}'};
@foreach(Webkul\Core\Models\Locale::all() as $locale)
row['{{ $locale->code }}'] = "{{ $option->translate($locale->code)['label'] }}";
@ -324,6 +330,10 @@
Vue.delete(this.optionRows, index);
},
adminName (row) {
return 'options[' + row.id + '][admin_name]';
},
localeInputName (row, locale) {
return 'options[' + row.id + '][' + locale + '][label]';
},

View File

@ -1,4 +0,0 @@
<div class="control-group">
<label for="sku">{{ $attribute->admin_name }}</label>
<input type="text" class="control" id="sku" name="sku"/>
</div>

View File

@ -100,7 +100,7 @@
<td>
@foreach($attribute->options as $option)
<span class="label">
<input type="hidden" name="super_attributes[{{$attribute->id}}][options][]" value="{{ $option->id }}"/>
<input type="hidden" name="super_attributes[{{$attribute->code}}][]" value="{{ $option->id }}"/>
{{ $option->label }}
<i class="icon cross-icon"></i>

View File

@ -26,9 +26,24 @@
@foreach($attributeGroup->attributes as $attribute)
@if(view()->exists($typeView = 'admin::catalog.products.attribute-types.' . $attribute->type))
@if(!$product->super_attributes->contains($attribute))
<?php
$validations = [];
if($attribute->is_required) {
array_push($validations, 'required');
}
@include ($typeView)
array_push($validations, $attribute->validation);
$validations = implode('|', array_filter($validations));
?>
@if(view()->exists($typeView = 'admin::catalog.products.field-types.' . $attribute->type))
@include ($typeView)
@endif
@endif

View File

@ -0,0 +1,16 @@
<div class="control-group" :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']">
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
</label>
<select v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}">
<option value="0" {{ !$product[$attribute->code] ? 'selected' : ''}}>
{{ __('admin::app.catalog.products.no') }}
</option>
<option value="1" {{ $product[$attribute->code] ? 'selected' : ''}}>
{{ __('admin::app.catalog.products.yes') }}
</option>
</select>
<span class="control-error" v-if="errors.has('{{ $attribute->code }}')">@{{ errors.first('{!! $attribute->code !!}') }}</span>
</div>

View File

@ -0,0 +1,9 @@
<div class="control-group" :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']">
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
</label>
<input type="text" v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}" value="{{ $product[$attribute->code]}}"/>
<span class="control-error" v-if="errors.has('{{ $attribute->code }}')">@{{ errors.first('{!! $attribute->code !!}') }}</span>
</div>

View File

@ -0,0 +1,11 @@
<div class="control-group" :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']">
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
</label>
<input type="text" v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}" value="{{ $product[$attribute->code]}}"/>
<datetime default="{{ $product[$attribute->code]}}"></datetime>
<span class="control-error" v-if="errors.has('{{ $attribute->code }}')">@{{ errors.first('{!! $attribute->code !!}') }}</span>
</div>

View File

@ -0,0 +1,17 @@
<div class="control-group" :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']">
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
</label>
<select v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}" multiple>
@foreach($attribute->options as $option)
<option value="{{ $option->id }}" {{ in_array($option->id, explode(',', $attribute[$attribute->code])) ? 'selected' : ''}}>
{{ $option->admin_name }}
</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('{{ $attribute->code }}')">@{{ errors.first('{!! $attribute->code !!}') }}</span>
</div>

View File

@ -0,0 +1,9 @@
<div class="control-group" :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']">
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
</label>
<input type="text" v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}" value="{{ $product[$attribute->code]}}"/>
<span class="control-error" v-if="errors.has('{{ $attribute->code }}')">@{{ errors.first('{!! $attribute->code !!}') }}</span>
</div>

View File

@ -0,0 +1,17 @@
<div class="control-group" :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']">
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
</label>
<select v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}">
@foreach($attribute->options as $option)
<option value="{{ $option->id }}" {{ $option->id == $attribute[$attribute->code] ? 'selected' : ''}}>
{{ $option->admin_name }}
</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('{{ $attribute->code }}')">@{{ errors.first('{!! $attribute->code !!}') }}</span>
</div>

View File

@ -0,0 +1,9 @@
<div class="control-group" :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']">
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
</label>
<input type="text" v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}" value="{{ $product[$attribute->code]}}"/>
<span class="control-error" v-if="errors.has('{{ $attribute->code }}')">@{{ errors.first('{!! $attribute->code !!}') }}</span>
</div>

View File

@ -0,0 +1,11 @@
<div class="control-group" :class="[errors.has('{{ $attribute->code }}') ? 'has-error' : '']">
<label for="{{ $attribute->code }}" {{ $attribute->is_required ? 'class=required' : '' }}>
{{ $attribute->admin_name }}
</label>
<textarea v-validate="'{{$validations}}'" class="control" id="{{ $attribute->code }}" name="{{ $attribute->code }}">
{{ $product[$attribute->code]}}
</textarea>
<span class="control-error" v-if="errors.has('{{ $attribute->code }}')">@{{ errors.first('{!! $attribute->code !!}') }}</span>
</div>

View File

@ -15,6 +15,7 @@ class CreateAttributeOptionsTable extends Migration
{
Schema::create('attribute_options', function (Blueprint $table) {
$table->increments('id');
$table->string('admin_name')->nullable();
$table->integer('sort_order')->nullable();
$table->integer('attribute_id')->unsigned();
$table->foreign('attribute_id')->references('id')->on('attributes')->onDelete('cascade');

View File

@ -12,6 +12,8 @@ class Attribute extends TranslatableModel
protected $fillable = ['code', 'admin_name', 'type', 'position', 'is_required', 'is_unique', 'value_per_locale', 'value_per_channel', 'is_filterable', 'is_configurable'];
protected $with = ['options'];
/**
* Get the options.
*/

View File

@ -7,4 +7,42 @@
return new Core;
}
}
if (! function_exists('array_permutation')) {
function array_permutation($input)
{
$results = [];
while (list($key, $values) = each($input)) {
if (empty($values)) {
continue;
}
if (empty($results)) {
foreach($values as $value) {
$results[] = [$key => $value];
}
} else {
$append = [];
foreach($results as &$result) {
$result[$key] = array_shift($values);
$copy = $result;
foreach($values as $item) {
$copy[$key] = $item;
$append[] = $copy;
}
array_unshift($values, $result[$key]);
}
$results = array_merge($results, $append);
}
}
return $results;
}
}
?>

View File

@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductAttributeValueTable extends Migration
class CreateProductAttributeValuesTable extends Migration
{
/**
* Run the migrations.
@ -13,7 +13,7 @@ class CreateProductAttributeValueTable extends Migration
*/
public function up()
{
Schema::create('product_attribute_value', function (Blueprint $table) {
Schema::create('product_attribute_values', function (Blueprint $table) {
$table->increments('id');
$table->string('locale')->nullable();
$table->text('text_value')->nullable();
@ -25,7 +25,7 @@ class CreateProductAttributeValueTable extends Migration
$table->json('json_value')->nullable();
$table->integer('product_id')->unsigned();
$table->integer('attribute_id')->unsigned();
$table->integer('channel_id')->unsigned();
$table->integer('channel_id')->nullable()->unsigned();
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->foreign('attribute_id')->references('id')->on('attributes')->onDelete('cascade');
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
@ -40,6 +40,6 @@ class CreateProductAttributeValueTable extends Migration
*/
public function down()
{
Schema::dropIfExists('product_attribute_value');
Schema::dropIfExists('product_attribute_values');
}
}

View File

@ -118,8 +118,6 @@ class ProductController extends Controller
{
$product = $this->product->findOrFail($id);
dd($product);
return view($this->_config['view'], compact('product'));
}

View File

@ -3,16 +3,31 @@
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Attribute\Models\Attribute;
use Webkul\Category\Models\Category;
use Webkul\Inventory\Models\InventorySource;
use Webkul\Attribute\Models\AttributeFamily;
use Webkul\Category\Models\Category;
use Webkul\Attribute\Models\Attribute;
use Webkul\Product\Models\ProductAttributeValue;
use Webkul\Inventory\Models\InventorySource;
class Product extends Model
{
protected $fillable = ['type', 'attribute_family_id', 'sku'];
protected $fillable = ['type', 'attribute_family_id', 'sku', 'parent_id'];
protected $with = ['super_attributes'];
protected $with = ['attribute_values', 'varients'];
/**
* @var array
*/
protected $attributeTypeFields = [
'text' => 'text_value',
'textarea' => 'text_value',
'price' => 'float_value',
'boolean' => 'boolean_value',
'select' => 'integer_value',
'multiselect' => 'text_value',
'datetime' => 'datetime_time',
'date' => 'date_value',
];
/**
* Get the product attribute family that owns the product.
@ -22,6 +37,22 @@ class Product extends Model
return $this->belongsTo(AttributeFamily::class);
}
/**
* Get the product attribute values that owns the product.
*/
public function attribute_values()
{
return $this->hasMany(ProductAttributeValue::class);
}
/**
* Get the product varients that owns the product.
*/
public function varients()
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* The categories that belong to the product.
*/
@ -70,11 +101,28 @@ class Product extends Model
return $this->belongsToMany(self::class, 'product_cross_sells');
}
public function __get($name) {
// if(array_key_exists($name, $this->data)) {
// return $this->data[$name];
// }
/**
* @return array
*/
public function attributesToArray()
{
$attributes = parent::attributesToArray();
return null;
$hiddenAttributes = $this->getHidden();
foreach ($this->attribute_values as $attributeValue) {
$attribute = $attributeValue->attribute;
if (in_array($attribute->code, $hiddenAttributes)) {
continue;
}
if ($value = $attributeValue[$this->attributeTypeFields[$attribute->type]]) {
$attributes[$attribute->code] = $value;
}
}
return $attributes;
}
}

View File

@ -5,15 +5,34 @@ namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Attribute\Models\Attribute;
use Webkul\Product\Models\Product;
use Webkul\Channel\Models\Channel;
class ProductAttributeValue extends Model
{
public $timestamps = false;
protected $with = ['attribute'];
protected $fillable = [
'product_id',
'attribute_id',
'channel_id',
'locale',
'text_value',
'boolean_value',
'integer_value',
'float_value',
'datetime_value',
'date_value',
'json_value'
];
/**
* Get the attribue that owns the attribute value.
* Get the attribute that owns the attribute value.
*/
public function attribue()
public function attribute()
{
return $this->belongsTo(Attribue::class);
return $this->belongsTo(Attribute::class);
}
/**
@ -23,4 +42,12 @@ class ProductAttributeValue extends Model
{
return $this->belongsTo(Product::class);
}
/**
* Get the channel that owns the attribute value.
*/
public function channel()
{
return $this->belongsTo(Channel::class);
}
}

View File

@ -2,6 +2,8 @@
namespace Webkul\Product\Repositories;
use Illuminate\Container\Container as App;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Core\Eloquent\Repository;
/**
@ -11,7 +13,41 @@ use Webkul\Core\Eloquent\Repository;
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ProductAttributeValueRepository extends Repository
{
{
/**
* AttributeRepository object
*
* @var array
*/
protected $attribute;
/**
* @var array
*/
protected $attributeTypeFields = [
'text' => 'text_value',
'textarea' => 'text_value',
'price' => 'float_value',
'boolean' => 'boolean_value',
'select' => 'integer_value',
'multiselect' => 'text_value',
'datetime' => 'datetime_time',
'date' => 'date_value',
];
/**
* Create a new controller instance.
*
* @param Webkul\Attribute\Repositories\AttributeRepository $attribute
* @return void
*/
public function __construct(AttributeRepository $attribute, App $app)
{
$this->attribute = $attribute;
parent::__construct($app);
}
/**
* Specify Model class name
*
@ -28,12 +64,10 @@ class ProductAttributeValueRepository extends Repository
*/
public function create(array $data)
{
$product = $this->model->create($data);
$attribute = $this->attribute->find($data['attribute_id']);
foreach ($data['super_attributes'] as $attributeId => $attribute) {
$product->super_attributes()->attach($attributeId);
}
$data[$this->attributeTypeFields[$attribute->type]] = $data['value'];
return $product;
return $this->model->create($data);
}
}

View File

@ -2,7 +2,10 @@
namespace Webkul\Product\Repositories;
use Illuminate\Container\Container as App;
use Webkul\Core\Eloquent\Repository;
use Webkul\Attribute\Repositories\AttributeRepository;
use Webkul\Product\Repositories\ProductAttributeValueRepository;
/**
* Product Reposotory
@ -12,6 +15,39 @@ use Webkul\Core\Eloquent\Repository;
*/
class ProductRepository extends Repository
{
/**
* AttributeRepository object
*
* @var array
*/
protected $attribute;
/**
* ProductAttributeValueRepository object
*
* @var array
*/
protected $attributeValue;
/**
* Create a new controller instance.
*
* @param Webkul\Attribute\Repositories\AttributeRepository $attribute
* @param Webkul\Attribute\Repositories\ProductAttributeValueRepository $attributeValue
* @return void
*/
public function __construct(
AttributeRepository $attribute,
ProductAttributeValueRepository $attributeValue,
App $app)
{
$this->attribute = $attribute;
$this->attributeValue = $attributeValue;
parent::__construct($app);
}
/**
* Specify Model class name
*
@ -31,11 +67,46 @@ class ProductRepository extends Repository
$product = $this->model->create($data);
if(isset($data['super_attributes'])) {
foreach ($data['super_attributes'] as $attributeId => $attribute) {
$product->super_attributes()->attach($attributeId);
$super_attributes = [];
foreach ($data['super_attributes'] as $attributeCode => $attributeOptions) {
$attribute = $this->attribute->findBy('code', $attributeCode);
$super_attributes[$attribute->id] = $attributeOptions;
$product->super_attributes()->attach($attribute->id);
}
foreach (array_permutation($super_attributes) as $permutation) {
$this->createVarient($product, $permutation);
}
}
return $product;
}
/**
* @param array $data
* @return mixed
*/
public function createVarient($product, $permutation)
{
$varient = $this->model->create([
'parent_id' => $product->id,
'type' => 'simple',
'attribute_family_id' => $product->attribute_family_id,
'sku' => $product->sku . '-varient-' . implode('-', $permutation),
]);
foreach($permutation as $attributeId => $optionId) {
$this->attributeValue->create([
'product_id' => $varient->id,
'attribute_id' => $attributeId,
'value' => $optionId
]);
}
return $varient;
}
}