208 lines
8.2 KiB
PHP
208 lines
8.2 KiB
PHP
<?php
|
|
|
|
namespace App\Models\Objects;
|
|
|
|
use App\Services\Filters\FiltersService;
|
|
use App\Support\UuidScopeTrait;
|
|
use App\Transformers\Objects\OptionTransformer;
|
|
use Faker\Generator;
|
|
use Illuminate\Database\Eloquent\Builder;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
use Illuminate\Database\Query\JoinClause;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class Field extends Model {
|
|
use UuidScopeTrait, SoftDeletes;
|
|
|
|
protected $dates = [
|
|
];
|
|
|
|
protected $fillable = [
|
|
'uuid',
|
|
'type',
|
|
'name',
|
|
'title',
|
|
'required',
|
|
'multiple',
|
|
'filterable',
|
|
'hidden',
|
|
'params'
|
|
];
|
|
|
|
protected $hidden = [
|
|
'id'
|
|
];
|
|
|
|
protected $casts = [
|
|
'params' => 'array'
|
|
];
|
|
|
|
|
|
public function groups(): BelongsToMany {
|
|
return $this->belongsToMany(FieldsGroup::class, 'fields_group_fields', 'field_id', 'group_id');
|
|
}
|
|
|
|
public function values(): HasMany {
|
|
return $this->hasMany(FieldType::CLASSES[$this->type]);
|
|
}
|
|
|
|
public function objectValues($objectId): HasMany {
|
|
return $this->values()->where(['object_id' => $objectId])->orderBy('ord');
|
|
}
|
|
|
|
public function objectsValues(array $ids): HasMany {
|
|
return $this->values()->whereIn('object_id', $ids);
|
|
}
|
|
|
|
public function objectTypeValues($objectTypeId): HasMany {
|
|
return $this->values()->whereHas('object', function($query) use($objectTypeId) {
|
|
$query->where(['type_id' => $objectTypeId]);
|
|
});
|
|
}
|
|
|
|
|
|
|
|
public function getOptionsAttribute(): ?Collection {
|
|
if (($this->type === FieldType::RELATION) && !empty($this->params['options']['show'])) {
|
|
if ($model = $this->params['related'] ?? null) {
|
|
$query = $model::query();
|
|
collect($this->params['options']['whereHas'] ?? [])->each(function($val, $prop) use($query) {
|
|
$query->whereHas($prop, function($query) use($val) {
|
|
foreach ($val as $attr => $v) {
|
|
$query->where([$attr => $v]);
|
|
}
|
|
});
|
|
});
|
|
if ($where = $this->params['options']['where'] ?? null) $query->where($where);
|
|
return $query->get();
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function getTransformerAttribute() {
|
|
$transformer = $this->params['transformer'] ?? OptionTransformer::class;
|
|
return new $transformer;
|
|
}
|
|
|
|
public function getIsFileAttribute(): bool {
|
|
return in_array($this->type, [FieldType::DOCUMENT, FieldType::IMAGE]);
|
|
}
|
|
|
|
|
|
|
|
public function getRepresented($filters = [], ?FiltersService $service = null): ?Collection {
|
|
if ($model = $this->params['related'] ?? null) {
|
|
$query = $model::whereHas('relationValues', function($query) use($filters, $service) {
|
|
$query->where(['field_id' => $this->id])->whereHas('object', function($query) use($filters, $service) {
|
|
self::applyFilters($query, collect($filters)->except($this->name), $service);
|
|
});
|
|
});
|
|
return $query->get();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function getRange($filters = [], ?FiltersService $service = null): ?array {
|
|
if (in_array($this->type, [FieldType::INTEGER, FieldType::FLOAT, FieldType::DATE, FieldType::DATETIME])) {
|
|
$query = $this->values()->whereHas('object', function($query) use($filters, $service) {
|
|
self::applyFilters($query, collect($filters)->except($this->name), $service);
|
|
});
|
|
return ['min' => $query->min('value') ?? 0, 'max' => $query->max('value') ?? 0];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public function getValue($objectId) {
|
|
return $this->objectValues($objectId)->get()->map(function($value) {
|
|
return $value->get();
|
|
});
|
|
}
|
|
|
|
public function setValue($objectId, $value) {
|
|
if ($this->multiple) $this->objectValues($objectId)->delete();
|
|
if (!($value instanceof Collection)) $value = collect((is_array($value) && !Arr::isAssoc($value)) ? $value : [$value]);
|
|
$value->each(function($value, $ord) use($objectId) {
|
|
if ($this->multiple) $this->values()->create(['object_id' => $objectId, 'ord' => $ord])->set($value);
|
|
else $this->values()->firstOrCreate(['object_id' => $objectId])->set($value);
|
|
});
|
|
return $this->getValue($objectId);
|
|
}
|
|
|
|
public function addValue($objectId, $value) {
|
|
if ($this->multiple) {
|
|
$max = $this->objectValues($objectId)->max('ord');
|
|
if (is_null($max)) $max = -1;
|
|
if (!($value instanceof Collection)) $value = collect((is_array($value) && !Arr::isAssoc($value)) ? $value : [$value]);
|
|
$value->each(function($value, $ord) use($objectId, $max) {
|
|
$this->values()->create(['object_id' => $objectId, 'ord' => $max + $ord + 1])->set($value);
|
|
});
|
|
}
|
|
return $this->getValue($objectId);
|
|
}
|
|
|
|
|
|
public static function applyFilters(Builder $query, Collection $filters, ?FiltersService $service = null) {
|
|
if ($types = $filters->get('types')) {
|
|
$types = is_array($types) ? $types : [$types];
|
|
$filters->forget('types');
|
|
$query->whereHas('type', function($query) use($types) {
|
|
$query->whereIn('uuid', $types)->orWhereIn('name', $types);
|
|
});
|
|
$filters->filter(function($value) {return $value;})->each(function($value, $prop) use($query, $types, $service) {
|
|
$field = Field::query()->where(['name' => $prop])->whereHas('groups.objectType', function($query) use($types) {
|
|
$query->whereIn('uuid', $types)->orWhereIn('name', $types);
|
|
})->first();
|
|
if ($field) $field->applyFilter($query, $value);
|
|
});
|
|
if ($service && $service->objectRelationName) $query->whereHas($service->objectRelationName, function($query) use($service, $filters) {
|
|
if (method_exists($service, 'applyNativeFilters')) $service->applyNativeFilters($query, $filters);
|
|
if (method_exists($service, 'applyPermissionsFilters')) $service->applyPermissionsFilters($query);
|
|
});
|
|
}
|
|
}
|
|
|
|
public function applyFilter($query, $value) {
|
|
if ($value = $this->prepareFilterValue($value)) $query->whereHas("{$this->type}Values", function($query) use($value) {
|
|
$query->where(['field_id' => $this->id]);
|
|
$class = FieldType::CLASSES[$this->type];
|
|
$class::applyFilter($query, $value);
|
|
});
|
|
}
|
|
|
|
public function prepareFilterValue($value) {
|
|
return is_array($value) ? collect($value)->filter(function($val) {
|
|
return !is_null($val);
|
|
})->all() : $value;
|
|
}
|
|
|
|
|
|
public function applyOrder(Builder $query, $dir) {
|
|
if ($table = FieldType::TABLES[$this->type] ?? null) {
|
|
$query->leftJoin("{$table} as prop-{$this->name}", function(JoinClause $join) use($table) {
|
|
$join->on('objects.id', '=', "prop-{$this->name}.object_id");
|
|
})->where(["prop-{$this->name}.field_id" => $this->id])->orderBy("prop-{$this->name}.value", $dir);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
public function fakeValue($objectId) {
|
|
$faker = new Generator();
|
|
if ($this->type === FieldType::STRING) $value = $faker->sentence(4);
|
|
elseif ($this->type === FieldType::TEXT) $value = $faker->realText(rand(50, 240));
|
|
elseif ($this->type === FieldType::INTEGER) $value = rand(0, 500);
|
|
elseif ($this->type === FieldType::FLOAT) $value = $faker->randomFloat(1, 0, 100);
|
|
elseif ($this->type === FieldType::BOOLEAN) $value = boolval(rand(0,1));
|
|
elseif ($this->type === FieldType::DATE) $value = $faker->dateTimeBetween('-5 years', '+ 3 years');
|
|
elseif ($this->type === FieldType::DATETIME) $value = $faker->dateTimeBetween('-5 years', '+ 3 years');
|
|
elseif ($this->type === FieldType::RELATION) $value = $this->options->random(($this->multiple) ? rand(1, 3) : 1);
|
|
$this->setValue($objectId, $value ?? null);
|
|
}
|
|
|
|
} |