Files
snipe-it/app/Models/Traits/HasCustomFields.php
T
Brady Wetherington d2b7828569 This is a squashed branch of all of the various commits that make up the new HasCustomFields trait.
This should allow us to add custom fields to just about anything we want to within Snipe-IT.

Below are the commits that have been squashed together:

Initial decoupling of custom field behavior from Assets for re-use

Add new DB columns to Custom Fields and fieldsets for 'type'

WIP: trying to figure out UI for custom fields for things other than Assets, find problematic places

Real progress towards getting to where this stuff might actually work...

Fix the table-name determining code for Custom Fields

Getting it closer to where Assets at least work

Rename the trait to it's new, even better name

Solid progress on the new Trait!

WIP: HasCustomFields, still working some stuff out

Got some basics working; creating custom fields and stuff

HasCustomFields now validates and saves

Starting to yank the other boilerplate code as things start to work (!)

Got the start of defaultValuesForCustomField() working

More progress (squash me!)

Add migrations for default_values_for_custom_fields table

WIP: more towards hasCustomFields trait

Progress cleaning up the PR, fixing FIXME's

New, passing HasCustomFieldsTrait test!

Fix date formatter helper for custom fields

Fixed more FIXME's
2024-06-06 13:35:38 +01:00

138 lines
5.2 KiB
PHP

<?php
namespace App\Models\Traits;
use App\Models\CustomField;
use App\Models\CustomFieldset;
use Illuminate\Support\Collection;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Event;
use App\Models\DefaultValuesForCustomFields;
/*********************************
* Trait HasCustomFields
* @package App\Models\Traits
*
* How to use: declare a PHP function getFieldset that will return your fieldset (not the ID, the actual set)
*
*/
trait HasCustomFields
{
protected static function bootHasCustomFields()
{
// https://tech.chrishardie.com/2022/define-fire-listen-custom-laravel-model-events-trait/
static::registerModelEvent('validating', function ($model, $event) {
\Log::debug("Uh, something happened? Something good, maybe?");
\Log::debug("model: $model, event: $event");
self::augmentValidationRulesForCustomFields($model);
});
}
/***************
* @return CustomFieldset|null
*
* This function by default will use the "getFieldsetKey()" method to
* return the customFieldset (or null) for this particular item. If
* necessary, you can override this method if your getFieldsetKey()
* cannot respond to `->fieldset` or `->id`.
*/
public function getFieldset(): ?CustomFieldset {
$pivot = $this->getFieldsetKey();
if(is_int($pivot)) { //why does this look just like the other thing? (below, look for is_int()
return Fieldset::find($pivot);
}
return $pivot->fieldset;
}
/**********************
* @return Object|int|null
* (if this is in PHP 8.0, can we just put that as the signature?)
*
* This is the main method you have to override. It should either return an
* Object who you can call `->fieldset` on and get a fieldset object, and also
* be able to call `->id` on to get a unique key to be able to show custom fields.
* For example, for Assets, the element that is returned is the 'model' for the Asset.
* For something like Users, which will probably have only one universal set of custom fields,
* it should just return the Fieldset ID for it. Or, if there are no custom fields, it should
* return null
*/
abstract public function getFieldsetKey(): Object|int|null; // php v8 minimum, GOOD. TODO
/***********************
* @param int $fieldset_id
* @return Collection
*
* This is the main method you need to override to return a list of things that are *using* this fieldset
* The format is an array with keys: a URL, and values. So, for assets, it might return
* {
* "models/14" => "MacBook Pro 13 (model no: 12345)"
* }
*/
abstract public static function getFieldsetUsers(int $fieldset_id): array;
public static function augmentValidationRulesForCustomFields($model) {
\Log::debug("Augmenting validation rules for custom fields!!!!!!");
$fieldset = $model->getFieldset();
if ($fieldset) {
foreach ($fieldset->fields as $field){
if($field->format == 'BOOLEAN'){ // TODO - this 'feels' like entanglement of concerns?
$model->{$field->db_column} = filter_var($model->{$model->db_column}, FILTER_VALIDATE_BOOLEAN);
}
}
if(!$model->rules) {
$model->rules = [];
}
$model->rules += $model->getFieldset()->validation_rules();
\Log::debug("FINAL RULES ARE: ".print_r($model->rules,true));
}
}
public function getDefaultValue(CustomField $field)
{
$pivot = $this->getFieldsetKey(); // TODO - feels copypasta-ish?
$key_id = null;
if( is_int($pivot) ) { // TODO: *WHY* does this code repeat?!
$key_id = $pivot; // now we're done
} elseif( is_object($pivot) ) {
$key_id = $pivot?->id;
}
if(is_null($key_id)) {
return;
}
// TODO - begninng to think my custom scope really should be just an integer :/
return DefaultValuesForCustomFields::where('type',self::class)
->where('custom_field_id',$field->id)
->where('item_pivot_id',$key_id)->first()?->default_value;
}
public function customFill(Request $request, User $user, bool $shouldSetDefaults = false) {
$this->_filled = true;
if ($this->getFieldset()) {
foreach ($this->getFieldset()->fields as $field) {
if (is_array($request->input($field->db_column))) {
$field_value = implode(', ', $request->input($field->db_column));
} else {
$field_value = $request->input($field->db_column);
}
if ($shouldSetDefaults && (is_null($field_value) || $field_value === '')) {
$field_value = $this->getDefaultValue($field);
}
if ($field->field_encrypted == '1') {
if ($user->can('admin')) {
$this->{$field->db_column} = \Crypt::encrypt($field_value);
}
} else {
$this->{$field->db_column} = $request->input($field->db_column);
}
}
}
}
}