-
Notifications
You must be signed in to change notification settings - Fork 5
Support parsing custom field values and attach to model during retrieval #8
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
3185013
c9f9175
ab23fe8
9e5792d
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 |
---|---|---|
|
@@ -15,12 +15,30 @@ Custom fields can be utilized to extend a model and offer a more flexible approa | |
|
||
## Features | ||
|
||
- PSR-4 autoloading compliant structure; | ||
- PSR-2 compliant code style; | ||
- Unit-Testing with PHPUnit 6; | ||
- Comprehensive guide and tutorial; | ||
- Easy to use with any framework or even a plain php file; | ||
- Useful tools for better code included. | ||
- **Dynamic Custom Fields**: The package allows you to define and manage custom fields for your models dynamically. You can add, update, and remove custom fields without modifying the underlying database structure. | ||
|
||
- **Polymorphic Relationship**: The package supports a polymorphic relationship between custom fields and the models that provide the context. This means you can associate custom fields with different types of models, providing flexibility and customization in managing and retrieving custom field data based on the context. | ||
|
||
- **Flexible Field Types**: The package supports various field types, including string, integer, float, datetime, select, and boolean. You can choose the appropriate field type based on your requirements. | ||
|
||
- **Validation**: Custom fields can be validated using Laravel's validation rules. You can define validation rules for each custom field to ensure the entered values meet the specified criteria. | ||
|
||
- **Default Values**: You can set default values for custom fields, which will be automatically populated if no value is provided during data entry. | ||
|
||
- **User-Friendly Naming**: Custom fields can be assigned user-friendly names to enhance readability and usability within your application. | ||
|
||
- **Description**: You can provide descriptions for custom fields to give more context and guidance to users when interacting with those fields. | ||
|
||
- **Easy Access to Custom Field Values**: With this package, accessing custom field values is as simple as accessing regular model attributes. You can use the familiar `$model->customField` syntax to retrieve the value of a custom field, making it seamless and intuitive to work with custom field data alongside other model properties. | ||
|
||
- **Easy Integration**: The package is designed to seamlessly integrate with your existing Laravel application. It follows Laravel's conventions and leverages Laravel's features and functionalities to provide a smooth development experience. | ||
|
||
- **Observer Support**: The package includes an observer that automatically updates the custom field values on the associated model whenever changes are made. This ensures consistency and synchronization between the custom fields and their corresponding values. | ||
|
||
- **Validation and Error Handling**: The package provides validation capabilities for custom fields, allowing you to validate user input based on the defined rules. It also handles error handling and validation messages in line with Laravel's validation system. | ||
|
||
- **Extensibility**: The package is built with extensibility in mind. You can easily extend and customize its functionality to meet your specific needs by leveraging Laravel's powerful features such as model events, custom validation rules, and more. | ||
|
||
|
||
## Installation | ||
|
||
|
@@ -35,7 +53,106 @@ Publish migration files and run command to build tables needed in the package | |
php artisan vendor:publish --tag="custom-fields-migrations" | ||
``` | ||
|
||
Run the migration to create the necessary tables in the database | ||
|
||
``` | ||
php artisan migrate | ||
``` | ||
|
||
## Usage | ||
### Defining Custom Fields | ||
|
||
### Custom field attributes | ||
|
||
- `friendly_name`: A user-friendly name for the custom field. | ||
- `key`: A unique name for the custom field. | ||
- `type`: The type of the custom field. It can be one of the following: `string`, `integer`, `float`, `datetime`, `select`, `boolean`. | ||
- `available_options`: The available options for a custom field of type `select`. | ||
- `required`: Indicates whether the custom field is required. The default value is `false`. | ||
- `default_value`: The default value for the custom field. | ||
- `description`: A description of the custom field. | ||
- `model_class`: The namespace of the model to which the custom field applies. | ||
- `contextable_id`: This field is used to store the ID of the related model that provides the context for the custom fields. It acts as a foreign key to establish the relationship between the custom fields and the model that defines the context. | ||
For example, if you have a Lead model that has custom fields and the context for those fields is provided by an associated Account model, the contextable_id field will store the ID of the associated Account. | ||
- `contextable_type`: This field is used to store the class name or type of the related model that provides the context for the custom fields. It helps identify the specific model class that defines the context for the custom fields. Continuing with the previous example, the contextable_type field will store the class name or type of the associated Account model. | ||
|
||
- Together, `contextable_id` and `contextable_type` provide a way to establish a polymorphic relationship between the custom fields and the model that defines the context. They allow you to associate custom fields with different types of models, providing flexibility and customization in managing and retrieving custom field data based on the context. | ||
|
||
Here's an example of the Lead model and Account model code snippets along with their usage: | ||
|
||
|
||
1. To define custom fields for lead model, we need to use the `Customizable` trait in the Lead model class. | ||
```php | ||
use OnrampLab\CustomFields\Concerns\Customizable; | ||
|
||
class Lead extends Model | ||
{ | ||
use Customizable; | ||
|
||
protected $guarded = []; | ||
|
||
public function account() | ||
{ | ||
return $this->belongsTo(Account::class); | ||
} | ||
|
||
public function getContext(): Model | ||
{ | ||
return $this->account; | ||
} | ||
} | ||
|
||
``` | ||
2. Since the context for those fields is provided by an associated Account model, we need to use `Contextable` trait in the Account model class and overwrite the `getContext` method of the `Customizable`trait. | ||
By overriding the `getContext()` method in the Lead model, we specify that the context for the Lead model is the associated Account model. This ensures that the custom fields defined for the Lead model are associated with the respective Account. | ||
|
||
|
||
|
||
```php | ||
use OnrampLab\CustomFields\Concerns\Contextable; | ||
|
||
class Account extends Model | ||
{ | ||
use Contextable; | ||
|
||
public function leads() | ||
{ | ||
return $this->hasMany(Lead::class); | ||
} | ||
} | ||
|
||
``` | ||
|
||
```php | ||
use OnrampLab\CustomFields\Concerns\Customizable; | ||
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 這段code sample跟上面重複了? |
||
|
||
class Lead extends Model | ||
{ | ||
use Customizable; | ||
|
||
protected $guarded = []; | ||
|
||
public function account() | ||
{ | ||
return $this->belongsTo(Account::class); | ||
} | ||
|
||
public function getContext(): Model | ||
{ | ||
return $this->account; | ||
} | ||
} | ||
|
||
``` | ||
|
||
After setting up the above configurations, you can proceed with the following steps: | ||
|
||
1. Define custom fields using the `CustomField` model. | ||
2. Create leads as usual, but with additional custom fields as you defined in the first step. | ||
3. If the custom field value passes validation, it will be stored in the `CustomFieldValue` table. | ||
4. Retrieve custom field values just like any other attribute of the Lead model. For example, you can access a custom field value using `$lead->customField`. | ||
|
||
By following these steps, you can seamlessly work with custom fields for the Lead model and access their values as if they were regular attributes. | ||
|
||
|
||
## Running Tests: | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,9 @@ trait Customizable | |
{ | ||
protected array $validatedCustomFieldValues = []; | ||
|
||
/** | ||
* Boot Model Observer. | ||
*/ | ||
public static function bootCustomizable(): void | ||
{ | ||
static::observe(ModelObserver::class); | ||
|
@@ -53,11 +56,10 @@ public function validateCustomFields(): void | |
$this->validatedCustomFieldValues = $validator->validate(); | ||
} | ||
|
||
protected function getTableColumns(): Collection | ||
{ | ||
return Collection::make(Schema::getColumnListing($this->getTable())); | ||
} | ||
|
||
/** | ||
* Get the custom fields associated with the model. | ||
*/ | ||
public function getCustomFields(): Collection | ||
{ | ||
$context = $this->getCustomFieldContext(); | ||
|
@@ -76,11 +78,17 @@ public function getCustomFields(): Collection | |
return $customFields; | ||
} | ||
|
||
/** | ||
* Get the context model associated with the model. | ||
*/ | ||
public function getCustomFieldContext(): ?Model | ||
{ | ||
return null; | ||
} | ||
|
||
/** | ||
* Save the custom field values. | ||
*/ | ||
public function saveCustomFieldValues(): void | ||
{ | ||
$validatedCustomFieldValues = $this->validatedCustomFieldValues; | ||
|
@@ -101,4 +109,27 @@ public function saveCustomFieldValues(): void | |
} | ||
}); | ||
} | ||
|
||
/** | ||
* Load custom field values and set them as model attributes. | ||
*/ | ||
public function loadCustomFieldValues(): void | ||
{ | ||
if (! $this->customFieldValues) { | ||
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. 這個attribute拿出來應該是個collection?collection就算是空的做boolean判斷也還會是true,所以可能要用collection的方法去判斷是否為空 |
||
return; | ||
} | ||
|
||
$this->customFieldValues->each(function (CustomFieldValue $customFieldValue) { | ||
$attribute = $customFieldValue->customField->key; | ||
$this->setAttribute($attribute, $customFieldValue->customField->parseValue($customFieldValue->value)); | ||
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 如果改用 |
||
}); | ||
} | ||
|
||
/** | ||
* Get the table columns of the model. | ||
*/ | ||
protected function getTableColumns(): Collection | ||
{ | ||
return Collection::make(Schema::getColumnListing($this->getTable())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,4 +14,10 @@ public function getValidationRule(): array | |
$this->key => ['boolean'], | ||
]; | ||
} | ||
|
||
public function parseValue($value): bool | ||
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 參數也要給mixed type嗎? |
||
{ | ||
return filter_var($value, FILTER_VALIDATE_BOOLEAN); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
|
||
namespace OnrampLab\CustomFields\Models; | ||
|
||
use Carbon\Carbon; | ||
use Parental\HasParent; | ||
|
||
class DateTimeCustomField extends CustomField | ||
|
@@ -14,4 +15,8 @@ public function getValidationRule(): array | |
$this->key => ['date'], | ||
]; | ||
} | ||
public function parseValue($value): string | ||
{ | ||
return Carbon::parse($value)->toDateTimeString(); | ||
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 這樣感覺大部分情況轉換前後都是一樣?要考慮回傳Carbona嗎? |
||
} | ||
} |
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
這個有點像業務在銷售某個產品哈哈哈 😆