add: full multi-tenancy control
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
];
|
||||
5
packages/Webkul/Attribute/src/Contracts/Attribute.php
Normal file
5
packages/Webkul/Attribute/src/Contracts/Attribute.php
Normal file
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Contracts;
|
||||
|
||||
interface Attribute {}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Contracts;
|
||||
|
||||
interface AttributeOption {}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Contracts;
|
||||
|
||||
interface AttributeValue {}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('attributes', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('code');
|
||||
$table->string('name');
|
||||
$table->string('type');
|
||||
$table->string('lookup_type')->nullable();
|
||||
$table->string('entity_type');
|
||||
$table->integer('sort_order')->nullable();
|
||||
$table->string('validation')->nullable();
|
||||
$table->boolean('is_required')->default(0);
|
||||
$table->boolean('is_unique')->default(0);
|
||||
$table->boolean('quick_add')->default(0);
|
||||
$table->boolean('is_user_defined')->default(1);
|
||||
$table->unique(['code', 'entity_type']);
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('attributes');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('attribute_options', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('name')->nullable();
|
||||
$table->integer('sort_order')->nullable();
|
||||
$table->integer('attribute_id')->unsigned();
|
||||
$table->foreign('attribute_id')->references('id')->on('attributes')->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('attribute_options');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('attribute_values', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->string('entity_type')->default('leads');
|
||||
$table->text('text_value')->nullable();
|
||||
$table->boolean('boolean_value')->nullable();
|
||||
$table->integer('integer_value')->nullable();
|
||||
$table->double('float_value')->nullable();
|
||||
$table->datetime('datetime_value')->nullable();
|
||||
$table->date('date_value')->nullable();
|
||||
$table->json('json_value')->nullable();
|
||||
|
||||
$table->integer('entity_id')->unsigned();
|
||||
$table->integer('attribute_id')->unsigned();
|
||||
|
||||
$table->foreign('attribute_id')->references('id')->on('attributes')->onDelete('cascade');
|
||||
$table->unique(['entity_type', 'entity_id', 'attribute_id'], 'entity_type_attribute_value_index_unique');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('attribute_values');
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('attribute_values', function (Blueprint $table) {
|
||||
$table->string('unique_id')->nullable();
|
||||
});
|
||||
|
||||
$tablePrefix = DB::getTablePrefix();
|
||||
|
||||
DB::statement('UPDATE '.$tablePrefix."attribute_values SET unique_id = CONCAT(entity_id, '|', attribute_id)");
|
||||
|
||||
Schema::table('attribute_values', function (Blueprint $table) {
|
||||
$table->unique('unique_id');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('attribute_values', function (Blueprint $table) {
|
||||
$table->dropUnique(['unique_id']);
|
||||
|
||||
$table->dropColumn('unique_id');
|
||||
});
|
||||
}
|
||||
};
|
||||
35
packages/Webkul/Attribute/src/Models/Attribute.php
Normal file
35
packages/Webkul/Attribute/src/Models/Attribute.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Webkul\Attribute\Contracts\Attribute as AttributeContract;
|
||||
|
||||
class Attribute extends Model implements AttributeContract
|
||||
{
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'code',
|
||||
'name',
|
||||
'type',
|
||||
'entity_type',
|
||||
'lookup_type',
|
||||
'is_required',
|
||||
'is_unique',
|
||||
'quick_add',
|
||||
'validation',
|
||||
'is_user_defined',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the options.
|
||||
*/
|
||||
public function options()
|
||||
{
|
||||
return $this->hasMany(AttributeOptionProxy::modelClass());
|
||||
}
|
||||
}
|
||||
30
packages/Webkul/Attribute/src/Models/AttributeOption.php
Normal file
30
packages/Webkul/Attribute/src/Models/AttributeOption.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Webkul\Attribute\Contracts\AttributeOption as AttributeOptionContract;
|
||||
|
||||
class AttributeOption extends Model implements AttributeOptionContract
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'sort_order',
|
||||
'attribute_id',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attribute that owns the attribute option.
|
||||
*/
|
||||
public function attribute()
|
||||
{
|
||||
return $this->belongsTo(AttributeProxy::modelClass());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Models;
|
||||
|
||||
use Konekt\Concord\Proxies\ModelProxy;
|
||||
|
||||
class AttributeOptionProxy extends ModelProxy {}
|
||||
7
packages/Webkul/Attribute/src/Models/AttributeProxy.php
Normal file
7
packages/Webkul/Attribute/src/Models/AttributeProxy.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Models;
|
||||
|
||||
use Konekt\Concord\Proxies\ModelProxy;
|
||||
|
||||
class AttributeProxy extends ModelProxy {}
|
||||
85
packages/Webkul/Attribute/src/Models/AttributeValue.php
Executable file
85
packages/Webkul/Attribute/src/Models/AttributeValue.php
Executable file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Webkul\Activity\Traits\LogsActivity;
|
||||
use Webkul\Attribute\Contracts\AttributeValue as AttributeValueContract;
|
||||
|
||||
class AttributeValue extends Model implements AttributeValueContract
|
||||
{
|
||||
use LogsActivity;
|
||||
|
||||
/**
|
||||
* Disable the default timestamps.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
/**
|
||||
* Cast the attributes to their respective types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts = [
|
||||
'json_value' => 'array',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are fillable for the model.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'attribute_id',
|
||||
'text_value',
|
||||
'boolean_value',
|
||||
'integer_value',
|
||||
'float_value',
|
||||
'datetime_value',
|
||||
'date_value',
|
||||
'json_value',
|
||||
'entity_id',
|
||||
'entity_type',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that are used for logging activity.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $attributeTypeFields = [
|
||||
'text' => 'text_value',
|
||||
'textarea' => 'text_value',
|
||||
'price' => 'float_value',
|
||||
'boolean' => 'boolean_value',
|
||||
'select' => 'integer_value',
|
||||
'multiselect' => 'text_value',
|
||||
'checkbox' => 'text_value',
|
||||
'email' => 'json_value',
|
||||
'address' => 'json_value',
|
||||
'phone' => 'json_value',
|
||||
'lookup' => 'integer_value',
|
||||
'datetime' => 'datetime_value',
|
||||
'date' => 'date_value',
|
||||
'file' => 'text_value',
|
||||
'image' => 'text_value',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attribute that owns the attribute value.
|
||||
*/
|
||||
public function attribute()
|
||||
{
|
||||
return $this->belongsTo(AttributeProxy::modelClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parent entity model (leads, products, persons or organizations).
|
||||
*/
|
||||
public function entity()
|
||||
{
|
||||
return $this->morphTo();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Models;
|
||||
|
||||
use Konekt\Concord\Proxies\ModelProxy;
|
||||
|
||||
class AttributeValueProxy extends ModelProxy {}
|
||||
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Providers;
|
||||
|
||||
use Illuminate\Routing\Router;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
class AttributeServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot(Router $router)
|
||||
{
|
||||
$this->loadMigrationsFrom(__DIR__.'/../Database/Migrations');
|
||||
}
|
||||
|
||||
/**
|
||||
* Register services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
{
|
||||
$this->registerConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register package config.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function registerConfig()
|
||||
{
|
||||
$this->mergeConfigFrom(
|
||||
dirname(__DIR__).'/Config/attribute_lookups.php', 'attribute_lookups'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Providers;
|
||||
|
||||
use Webkul\Core\Providers\BaseModuleServiceProvider;
|
||||
|
||||
class ModuleServiceProvider extends BaseModuleServiceProvider
|
||||
{
|
||||
protected $models = [
|
||||
\Webkul\Attribute\Models\Attribute::class,
|
||||
\Webkul\Attribute\Models\AttributeOption::class,
|
||||
\Webkul\Attribute\Models\AttributeValue::class,
|
||||
];
|
||||
}
|
||||
18
packages/Webkul/Attribute/src/Repositories/AttributeOptionRepository.php
Executable file
18
packages/Webkul/Attribute/src/Repositories/AttributeOptionRepository.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Repositories;
|
||||
|
||||
use Webkul\Core\Eloquent\Repository;
|
||||
|
||||
class AttributeOptionRepository extends Repository
|
||||
{
|
||||
/**
|
||||
* Specify Model class name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return 'Webkul\Attribute\Contracts\AttributeOption';
|
||||
}
|
||||
}
|
||||
179
packages/Webkul/Attribute/src/Repositories/AttributeRepository.php
Executable file
179
packages/Webkul/Attribute/src/Repositories/AttributeRepository.php
Executable file
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Repositories;
|
||||
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Support\Str;
|
||||
use Webkul\Core\Eloquent\Repository;
|
||||
|
||||
class AttributeRepository extends Repository
|
||||
{
|
||||
/**
|
||||
* Create a new repository instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected AttributeOptionRepository $attributeOptionRepository,
|
||||
Container $container
|
||||
) {
|
||||
parent::__construct($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify Model class name
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return 'Webkul\Attribute\Contracts\Attribute';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Webkul\Attribute\Contracts\Attribute
|
||||
*/
|
||||
public function create(array $data)
|
||||
{
|
||||
$options = isset($data['options']) ? $data['options'] : [];
|
||||
|
||||
$attribute = $this->model->create($data);
|
||||
|
||||
if (in_array($attribute->type, ['select', 'multiselect', 'checkbox']) && count($options)) {
|
||||
$sortOrder = 1;
|
||||
|
||||
foreach ($options as $optionInputs) {
|
||||
$this->attributeOptionRepository->create(array_merge([
|
||||
'attribute_id' => $attribute->id,
|
||||
'sort_order' => $sortOrder++,
|
||||
], $optionInputs));
|
||||
}
|
||||
}
|
||||
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @param string $attribute
|
||||
* @return \Webkul\Attribute\Contracts\Attribute
|
||||
*/
|
||||
public function update(array $data, $id, $attribute = 'id')
|
||||
{
|
||||
$attribute = $this->find($id);
|
||||
|
||||
$attribute->update($data);
|
||||
|
||||
if (! in_array($attribute->type, ['select', 'multiselect', 'checkbox'])) {
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
if (! isset($data['options'])) {
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
foreach ($data['options'] as $optionId => $optionInputs) {
|
||||
$isNew = $optionInputs['isNew'] == 'true';
|
||||
|
||||
if ($isNew) {
|
||||
$this->attributeOptionRepository->create(array_merge([
|
||||
'attribute_id' => $attribute->id,
|
||||
], $optionInputs));
|
||||
} else {
|
||||
$isDelete = $optionInputs['isDelete'] == 'true';
|
||||
|
||||
if ($isDelete) {
|
||||
$this->attributeOptionRepository->delete($optionId);
|
||||
} else {
|
||||
$this->attributeOptionRepository->update($optionInputs, $optionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $attribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code
|
||||
* @return \Webkul\Attribute\Contracts\Attribute
|
||||
*/
|
||||
public function getAttributeByCode($code)
|
||||
{
|
||||
static $attributes = [];
|
||||
|
||||
if (array_key_exists($code, $attributes)) {
|
||||
return $attributes[$code];
|
||||
}
|
||||
|
||||
return $attributes[$code] = $this->findOneByField('code', $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $lookup
|
||||
* @param string $query
|
||||
* @param array $columns
|
||||
* @return array
|
||||
*/
|
||||
public function getLookUpOptions($lookup, $query = '', $columns = [])
|
||||
{
|
||||
$lookup = config('attribute_lookups.'.$lookup);
|
||||
|
||||
if (! count($columns)) {
|
||||
$columns = [($lookup['value_column'] ?? 'id').' as id', ($lookup['label_column'] ?? 'name').' as name'];
|
||||
}
|
||||
|
||||
if (Str::contains($lookup['repository'], 'UserRepository')) {
|
||||
$userRepository = app($lookup['repository'])->where('status', 1);
|
||||
|
||||
$currentUser = auth()->guard('user')->user();
|
||||
|
||||
if ($currentUser?->view_permission === 'group') {
|
||||
$query = urldecode($query);
|
||||
|
||||
$userIds = bouncer()->getAuthorizedUserIds();
|
||||
|
||||
return $userRepository
|
||||
->when(! empty($userIds), fn ($queryBuilder) => $queryBuilder->whereIn('users.id', $userIds))
|
||||
->when(! empty($query), fn ($queryBuilder) => $queryBuilder->where('users.name', 'like', "%{$query}%"))
|
||||
->get();
|
||||
} elseif ($currentUser?->view_permission === 'individual') {
|
||||
return $userRepository->where('users.id', $currentUser->id);
|
||||
}
|
||||
|
||||
return $userRepository->where('users.name', 'like', '%'.urldecode($query).'%')->get();
|
||||
}
|
||||
|
||||
return app($lookup['repository'])->findWhere([
|
||||
[$lookup['label_column'] ?? 'name', 'like', '%'.urldecode($query).'%'],
|
||||
], $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $lookup
|
||||
* @param int|array $entityId
|
||||
* @param array $columns
|
||||
* @return mixed
|
||||
*/
|
||||
public function getLookUpEntity($lookup, $entityId = null, $columns = [])
|
||||
{
|
||||
if (! $entityId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$lookup = config('attribute_lookups.'.$lookup);
|
||||
|
||||
if (! count($columns)) {
|
||||
$columns = [($lookup['value_column'] ?? 'id').' as id', ($lookup['label_column'] ?? 'name').' as name'];
|
||||
}
|
||||
|
||||
if (is_array($entityId)) {
|
||||
return app($lookup['repository'])->findWhereIn(
|
||||
'id',
|
||||
$entityId,
|
||||
$columns
|
||||
);
|
||||
} else {
|
||||
return app($lookup['repository'])->find($entityId, $columns);
|
||||
}
|
||||
}
|
||||
}
|
||||
267
packages/Webkul/Attribute/src/Repositories/AttributeValueRepository.php
Executable file
267
packages/Webkul/Attribute/src/Repositories/AttributeValueRepository.php
Executable file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Repositories;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Container\Container;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Webkul\Attribute\Contracts\AttributeValue;
|
||||
use Webkul\Core\Eloquent\Repository;
|
||||
|
||||
class AttributeValueRepository extends Repository
|
||||
{
|
||||
/**
|
||||
* Create a new repository instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected AttributeRepository $attributeRepository,
|
||||
Container $container
|
||||
) {
|
||||
parent::__construct($container);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify model class name.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function model()
|
||||
{
|
||||
return AttributeValue::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save attribute value.
|
||||
*/
|
||||
public function save(array $data, $attributes = []): void
|
||||
{
|
||||
if (empty($attributes)) {
|
||||
$conditions = ['entity_type' => $data['entity_type']];
|
||||
|
||||
if (isset($data['quick_add'])) {
|
||||
$conditions['quick_add'] = 1;
|
||||
}
|
||||
|
||||
$attributes = $this->attributeRepository->where($conditions)->get();
|
||||
}
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
$typeColumn = $this->model::$attributeTypeFields[$attribute->type];
|
||||
|
||||
if ($attribute->type === 'boolean') {
|
||||
$data[$attribute->code] = isset($data[$attribute->code]) && $data[$attribute->code] ? 1 : 0;
|
||||
}
|
||||
|
||||
if (! array_key_exists($attribute->code, $data)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($attribute->type === 'price'
|
||||
&& isset($data[$attribute->code])
|
||||
&& $data[$attribute->code] === ''
|
||||
) {
|
||||
$data[$attribute->code] = null;
|
||||
}
|
||||
|
||||
if ($attribute->type === 'date' && $data[$attribute->code] === '') {
|
||||
$data[$attribute->code] = null;
|
||||
}
|
||||
|
||||
if ($attribute->type === 'multiselect' || $attribute->type === 'checkbox') {
|
||||
$data[$attribute->code] = implode(',', $data[$attribute->code]);
|
||||
}
|
||||
|
||||
if ($attribute->type === 'email' || $attribute->type === 'phone') {
|
||||
$data[$attribute->code] = $this->sanitizeEmailAndPhone($data[$attribute->code]);
|
||||
}
|
||||
|
||||
if ($attribute->type === 'image' || $attribute->type === 'file') {
|
||||
$data[$attribute->code] = $data[$attribute->code] instanceof UploadedFile
|
||||
? $data[$attribute->code]->store($data['entity_type'].'/'.$data['entity_id'])
|
||||
: null;
|
||||
}
|
||||
|
||||
$attributeValue = $this->findOneWhere([
|
||||
'entity_type' => $data['entity_type'],
|
||||
'entity_id' => $data['entity_id'],
|
||||
'attribute_id' => $attribute->id,
|
||||
]);
|
||||
|
||||
if (! $attributeValue) {
|
||||
$this->create([
|
||||
'entity_type' => $data['entity_type'],
|
||||
'entity_id' => $data['entity_id'],
|
||||
'attribute_id' => $attribute->id,
|
||||
$typeColumn => $data[$attribute->code],
|
||||
]);
|
||||
} else {
|
||||
$this->update([
|
||||
$typeColumn => $data[$attribute->code],
|
||||
], $attributeValue->id);
|
||||
|
||||
if ($attribute->type == 'image' || $attribute->type == 'file') {
|
||||
if ($attributeValue->text_value) {
|
||||
Storage::delete($attributeValue->text_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is value unique.
|
||||
*
|
||||
* @param int $entityId
|
||||
* @param string $entityType
|
||||
* @param \Webkul\Attribute\Contracts\Attribute $attribute
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*/
|
||||
public function isValueUnique($entityId, $entityType, $attribute, $value)
|
||||
{
|
||||
$query = $this->resetScope()->model
|
||||
->where('attribute_id', $attribute->id)
|
||||
->where('entity_type', $entityType)
|
||||
->where('entity_id', '!=', $entityId);
|
||||
|
||||
/**
|
||||
* If the attribute type is email or phone, check the JSON value.
|
||||
*/
|
||||
if (in_array($attribute->type, ['email', 'phone'])) {
|
||||
$query->whereJsonContains($this->model::$attributeTypeFields[$attribute->type], [['value' => $value]]);
|
||||
} else {
|
||||
$query->where($this->model::$attributeTypeFields[$attribute->type], $value);
|
||||
}
|
||||
|
||||
return $query->get()->count() ? false : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removed null values from email and phone fields.
|
||||
*
|
||||
* @param array $data
|
||||
* @return array
|
||||
*/
|
||||
public function sanitizeEmailAndPhone($data)
|
||||
{
|
||||
foreach ($data as $key => $row) {
|
||||
if (is_null($row['value'])) {
|
||||
unset($data[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace placeholders with values
|
||||
*/
|
||||
public function getAttributeLabel(mixed $value, mixed $attribute)
|
||||
{
|
||||
switch ($attribute?->type) {
|
||||
case 'price':
|
||||
$label = core()->formatBasePrice($value);
|
||||
|
||||
break;
|
||||
|
||||
case 'boolean':
|
||||
$label = $value ? 'Yes' : 'No';
|
||||
|
||||
break;
|
||||
|
||||
case 'select':
|
||||
case 'radio':
|
||||
case 'lookup':
|
||||
if ($attribute->lookup_type) {
|
||||
$option = $this->attributeRepository->getLookUpEntity($attribute->lookup_type, $value);
|
||||
} else {
|
||||
$option = $attribute->options->where('id', $value)->first();
|
||||
}
|
||||
|
||||
$label = $option?->name;
|
||||
|
||||
break;
|
||||
|
||||
case 'multiselect':
|
||||
case 'checkbox':
|
||||
if ($attribute->lookup_type) {
|
||||
$options = $this->attributeRepository->getLookUpEntity($attribute->lookup_type, explode(',', $value));
|
||||
} else {
|
||||
$options = $attribute->options->whereIn('id', explode(',', $value));
|
||||
}
|
||||
|
||||
$optionsLabels = [];
|
||||
|
||||
foreach ($options as $key => $option) {
|
||||
$optionsLabels[] = $option->name;
|
||||
}
|
||||
|
||||
$label = implode(', ', $optionsLabels);
|
||||
|
||||
break;
|
||||
|
||||
case 'email':
|
||||
case 'phone':
|
||||
if (! is_array($value)) {
|
||||
break;
|
||||
}
|
||||
|
||||
$optionsLabels = [];
|
||||
|
||||
foreach ($value as $item) {
|
||||
$optionsLabels[] = $item['value'].' ('.$item['label'].')';
|
||||
}
|
||||
|
||||
$label = implode(', ', $optionsLabels);
|
||||
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
if (
|
||||
! $value
|
||||
|| ! count(array_filter($value))
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
||||
$label = $value['address'].'<br>'
|
||||
.$value['postcode'].' '.$value['city'].'<br>'
|
||||
.core()->state_name($value['state']).'<br>'
|
||||
.core()->country_name($value['country']).'<br>';
|
||||
|
||||
break;
|
||||
|
||||
case 'date':
|
||||
if ($value) {
|
||||
$label = Carbon::parse($value)->format('D M d, Y');
|
||||
} else {
|
||||
$label = null;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'datetime':
|
||||
if ($value) {
|
||||
$label = Carbon::parse($value)->format('D M d, Y H:i A');
|
||||
} else {
|
||||
$label = null;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($value instanceof Carbon) {
|
||||
$label = $value->format('D M d, Y H:i A');
|
||||
} else {
|
||||
$label = $value;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return $label ?? null;
|
||||
}
|
||||
}
|
||||
175
packages/Webkul/Attribute/src/Traits/CustomAttribute.php
Normal file
175
packages/Webkul/Attribute/src/Traits/CustomAttribute.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace Webkul\Attribute\Traits;
|
||||
|
||||
use Webkul\Attribute\Models\AttributeValueProxy;
|
||||
use Webkul\Attribute\Repositories\AttributeRepository;
|
||||
|
||||
trait CustomAttribute
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $attributeTypeFields = [
|
||||
'text' => 'text_value',
|
||||
'textarea' => 'text_value',
|
||||
'price' => 'float_value',
|
||||
'boolean' => 'boolean_value',
|
||||
'select' => 'integer_value',
|
||||
'multiselect' => 'text_value',
|
||||
'checkbox' => 'text_value',
|
||||
'email' => 'json_value',
|
||||
'address' => 'json_value',
|
||||
'phone' => 'json_value',
|
||||
'lookup' => 'integer_value',
|
||||
'datetime' => 'datetime_value',
|
||||
'date' => 'date_value',
|
||||
'file' => 'text_value',
|
||||
'image' => 'text_value',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the attribute values that owns the entity.
|
||||
*/
|
||||
public function attribute_values()
|
||||
{
|
||||
return $this->morphMany(AttributeValueProxy::modelClass(), 'entity');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an attribute from the model.
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function getAttribute($key)
|
||||
{
|
||||
if (! method_exists(static::class, $key) && ! isset($this->attributes[$key])) {
|
||||
if (isset($this->id)) {
|
||||
$this->attributes[$key] = '';
|
||||
|
||||
$attribute = app(AttributeRepository::class)->getAttributeByCode($key);
|
||||
|
||||
$this->attributes[$key] = $this->getCustomAttributeValue($attribute);
|
||||
|
||||
return $this->getAttributeValue($key);
|
||||
}
|
||||
}
|
||||
|
||||
return parent::getAttribute($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function attributesToArray()
|
||||
{
|
||||
$attributes = parent::attributesToArray();
|
||||
|
||||
$hiddenAttributes = $this->getHidden();
|
||||
|
||||
if (isset($this->id)) {
|
||||
$customAttributes = $this->getCustomAttributes();
|
||||
|
||||
foreach ($customAttributes as $attribute) {
|
||||
if (in_array($attribute->code, $hiddenAttributes) && isset($this->attributes[$attribute->code])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes[$attribute->code] = $this->getCustomAttributeValue($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check in loaded family attributes.
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
public function getCustomAttributes()
|
||||
{
|
||||
static $attributes;
|
||||
|
||||
if ($attributes) {
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
return $attributes = app(AttributeRepository::class)->where('entity_type', $this->getTable())->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an product attribute value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getCustomAttributeValue($attribute)
|
||||
{
|
||||
if (! $attribute) {
|
||||
return;
|
||||
}
|
||||
|
||||
$attributeValue = $this->attribute_values->where('attribute_id', $attribute->id)->first();
|
||||
|
||||
return $attributeValue[self::$attributeTypeFields[$attribute->type]] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the given model.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @return Collection
|
||||
*/
|
||||
public function getLookUpAttributes($attributes)
|
||||
{
|
||||
$attributes = app(AttributeRepository::class)->scopeQuery(function ($query) use ($attributes) {
|
||||
return $query->distinct()
|
||||
->where('type', 'lookup')
|
||||
->where('entity_type', request('entity_type'))
|
||||
->whereIn('code', array_keys($attributes, '', false));
|
||||
})->get();
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new instance of the given model.
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param bool $exists
|
||||
* @return static
|
||||
*/
|
||||
public function newInstance($attributes = [], $exists = false)
|
||||
{
|
||||
// $attributes = $this->getLookUpAttributes($attributes);
|
||||
|
||||
// Play with data here
|
||||
|
||||
return parent::newInstance($attributes, $exists);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the model with an array of attributes.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @throws \Illuminate\Database\Eloquent\MassAssignmentException
|
||||
*/
|
||||
public function fill(array $attributes)
|
||||
{
|
||||
// Play with data here
|
||||
|
||||
return parent::fill($attributes);
|
||||
}
|
||||
|
||||
// Delete model's attribute values
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function ($entity) {
|
||||
$entity->attribute_values()->delete();
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user