-
Notifications
You must be signed in to change notification settings - Fork 5
Support validate custom fields and save custom field values #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
56b9e04
f2e767a
de47f9c
4b9707c
3a4b573
fae920e
f8518a7
8b98840
f665dec
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<?php | ||
|
||
namespace OnrampLab\CustomFields\AttributeCastors; | ||
|
||
use InvalidArgumentException; | ||
use OnrampLab\CustomFields\Models\CustomField; | ||
use Illuminate\Support\Collection; | ||
use OnrampLab\CustomFields\ValueObjects\AvailableOption; | ||
|
||
class AvailableOptionsCastor | ||
{ | ||
/** | ||
* Cast the given value. | ||
* | ||
* @param CustomField $model | ||
* @param string $key | ||
* @param mixed $value | ||
* @param array $attributes | ||
* @return Collection<AvailableOption> | ||
*/ | ||
public function get($model, $key, $value, $attributes) | ||
{ | ||
$data = json_decode(data_get($attributes, 'available_options') ?? '[]', true); | ||
|
||
return Collection::make($data) | ||
->map(function (array $option) { | ||
return new AvailableOption($option); | ||
}); | ||
} | ||
|
||
/** | ||
* Prepare the given value for storage. | ||
* | ||
* @param CustomField $model | ||
* @param string $key | ||
* @param Collection|array $value | ||
* @param array $attributes | ||
* @return array | ||
*/ | ||
public function set($model, $key, $value, $attributes) | ||
{ | ||
$value = Collection::make($value); | ||
$isValid = $value->every(function ($option) { | ||
return $option instanceof AvailableOption; | ||
}); | ||
|
||
if (!$isValid) { | ||
throw new InvalidArgumentException('This given value is not an AvailableOption instance.'); | ||
} | ||
|
||
return [ | ||
'available_options' => json_encode($value->toArray()), | ||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<?php | ||
|
||
namespace OnrampLab\CustomFields\Concerns; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\MorphMany; | ||
use OnrampLab\CustomFields\Models\CustomField; | ||
|
||
/** | ||
* @mixin Model | ||
*/ | ||
trait Contextable | ||
{ | ||
/** | ||
* Retrieve model's custom fields | ||
*/ | ||
public function customFields(): MorphMany | ||
{ | ||
return $this->morphMany(CustomField::class, 'contextable'); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
<?php | ||
|
||
namespace OnrampLab\CustomFields\Concerns; | ||
|
||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\MorphMany; | ||
use Illuminate\Support\Collection; | ||
use Illuminate\Support\Facades\Schema; | ||
use Illuminate\Support\Facades\Validator; | ||
use OnrampLab\CustomFields\Models\CustomField; | ||
use OnrampLab\CustomFields\Models\CustomFieldValue; | ||
use OnrampLab\CustomFields\Observers\ModelObserver; | ||
|
||
/** | ||
* @mixin Model | ||
*/ | ||
trait Customizable | ||
{ | ||
protected ?array $validatedCustomFieldValues = []; | ||
|
||
public static function bootCustomizable(): void | ||
{ | ||
static::observe(ModelObserver::class); | ||
} | ||
|
||
/** | ||
* Retrieve model's custom field values | ||
*/ | ||
public function customFieldValues(): MorphMany | ||
{ | ||
return $this->morphMany(CustomFieldValue::class, 'customizable'); | ||
} | ||
|
||
/** | ||
* Validate custom field values | ||
*/ | ||
public function validateCustomFields(): void | ||
{ | ||
$customFields = $this->getCustomFields(); | ||
if ($customFields->isEmpty()) { | ||
return; | ||
} | ||
$tableColumns = $this->getTableColumns(); | ||
$modelAttributes = Collection::make($this->getAttributes()); | ||
$modelAttributeKeys = $modelAttributes->keys(); | ||
$customFieldColumns = $modelAttributeKeys->diff($tableColumns); | ||
$customFieldsRules = $customFields->flatMap(function (CustomField $field) { | ||
return $field->getValidationRule(); | ||
})->all(); | ||
|
||
$customFieldValues = $modelAttributes->only($customFieldColumns)->toArray(); | ||
$validator = Validator::make($customFieldValues, $customFieldsRules); | ||
$this->validatedCustomFieldValues = $validator->validate(); | ||
$this->setRawAttributes($modelAttributes->only($tableColumns)->toArray()); | ||
} | ||
|
||
protected function getTableColumns(): Collection | ||
{ | ||
return Collection::make(Schema::getColumnListing($this->getTable())); | ||
} | ||
|
||
public function getCustomFields(): Collection | ||
{ | ||
$context = $this->getContext(); | ||
$query = CustomField::query(); | ||
if (is_null($context)) { | ||
$customFields = $query->get(); | ||
} else { | ||
$customFields = $query | ||
->where('contextable_type', get_class($context)) | ||
->where('contextable_id', $context->id) | ||
->get(); | ||
} | ||
|
||
return $customFields; | ||
} | ||
|
||
public function getContext(): ?Model | ||
moon-brillar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
return null; | ||
} | ||
|
||
public function saveCustomFieldValues(): void | ||
{ | ||
$validatedCustomFieldValues = $this->validatedCustomFieldValues; | ||
if (empty($validatedCustomFieldValues)) { | ||
return; | ||
} | ||
$customFields = $this->getCustomFields(); | ||
$customFields->each(function ($customField) use ($validatedCustomFieldValues) { | ||
if (array_key_exists($customField->key, $validatedCustomFieldValues)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 comment 可以考慮用guard pattern? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 可以! |
||
$value = $validatedCustomFieldValues[$customField->key]; | ||
$constraints = [ | ||
'custom_field_id' => $customField->id, | ||
'customizable_type' => get_class($this), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 comment 多型關聯的class name如同上面提到的 |
||
'customizable_id' => $this->id | ||
]; | ||
$values = ['value' => $value]; | ||
CustomFieldValue::updateOrCreate($constraints, $values); | ||
} | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 comment 這裡更新完後要把 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 這邊應該是不用 (? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace OnrampLab\CustomFields\Models; | ||
|
||
use Parental\HasParent; | ||
|
||
class BooleanCustomField extends CustomField | ||
{ | ||
use HasParent; | ||
|
||
public function getValidationRule(): array | ||
{ | ||
return [ | ||
$this->key => ['boolean'], | ||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ | |
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
use Illuminate\Database\Eloquent\Factories\Factory; | ||
use Illuminate\Database\Eloquent\Model; | ||
use Illuminate\Database\Eloquent\Relations\BelongsTo; | ||
use OnrampLab\CustomFields\Database\Factories\CustomFieldValueFactory; | ||
|
||
class CustomFieldValue extends Model | ||
|
@@ -19,12 +20,17 @@ class CustomFieldValue extends Model | |
protected $fillable = [ | ||
'custom_field_id', | ||
'value', | ||
'model_id', | ||
'model_type', | ||
'customizable_id', | ||
'customizable_type', | ||
]; | ||
|
||
protected static function newFactory(): Factory | ||
{ | ||
return CustomFieldValueFactory::new(); | ||
} | ||
|
||
public function customField(): BelongsTo | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 comment 可能也可以叫做field就好? |
||
{ | ||
return $this->belongsTo(CustomField::class); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace OnrampLab\CustomFields\Models; | ||
|
||
use Parental\HasParent; | ||
|
||
class DateTimeCustomField extends CustomField | ||
{ | ||
use HasParent; | ||
|
||
public function getValidationRule(): array | ||
{ | ||
return [ | ||
$this->key => ['date'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 comment 在想這個會不會太寬鬆,之後可以討論一下 |
||
]; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
<?php | ||
|
||
namespace OnrampLab\CustomFields\Models; | ||
|
||
use Parental\HasParent; | ||
|
||
class FloatCustomField extends CustomField | ||
{ | ||
use HasParent; | ||
|
||
public function getValidationRule(): array | ||
{ | ||
return [ | ||
$this->key => ['numeric'], | ||
]; | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❗ change request
這邊應該會有一個共同條件是modal class才對?不管context有沒有存在都需要的條件,組成query應該會如下所示:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💬 comment
在拿多型關聯的class name時會建議用modal身上的
getMorphClass
去拿會比較好,因為多型關聯用的class name是可以客製的https://laravel.com/docs/8.x/eloquent-relationships#custom-polymorphic-types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed, thanks!!