QR_code_generator/app/Models/Objects/Field.php

204 lines
8.1 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 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);
}
}