Attribute
This module allows for user-made attributes to be appended to (resource) as fields.
Every attribute acts like a normal field; they are added to forms, detail page and the index page.
Installation
To install this module:
composer require qore/attribute
php artisan vendor:publish --tag=qore.attribute.db
php artisan vendor:publish --tag=qore.attribute.frontend
php artisan vendor:publish --tag=qore.attribute.config
Usage
Make sure to migrate (php artisan tenants:migrate
)
Getting started
In the published config file attribute.php
, you need to configure which resources should allow for attributes:
/**
* The resourced allowed to have attributes
*/
'resources' => [
// MyResource::class
],
After enabling the module, you should be ready to use attributes now.
Creating a new attribute
In the interface, you can start creating attributes by going to /resources/attributes
. You can also add a menu item:
$tab->addResourceMenuItem(resource(AttributeResource::class));
When you create an attribute, you can select a type. These types are defined in the config attribute.php
file.
You can add your own types here:
'types' => fn () => [
[
'label' => __('attribute::attribute.Text'),
'value' => AttributeType::TEXT->value
],
[
'label' => __('attribute::attribute.Checkbox'),
'value' => AttributeType::CHECKBOX->value
],
[
'label' => __('attribute::attribute.Dropdown'),
'value' => AttributeType::SELECT->value
],
[
'label' => __('attribute::attribute.Multiselect'),
'value' => AttributeType::MULTISELECT->value
]
],
Note that if you add a type to the config file, you should also add a custom factory
and add it to the factories
array
Automatically add attributes to your resource
By default, if you create an attribute, select a resource, and mark them as active (with the checkbox), they should automatically be added to your resource.
To prevent auto-adding attributes to your resource, set this to false in the config file:
'add_fields_automatically' => false,
Note that if you use a fieldLayout
in a resource, you will need to add your attribute names manually to the fieldLayout array.
Manually append attributes to your fieldLayout
You can add attributes to your fieldLayout
like this:
public function fieldLayout(bool $forDetail, ?Model $model = null): ?FieldLayout
{
$cards = [
'Detail' => [
'gender',
['first_name', 'last_name']
],
'Attributes' => [
// Attributes will be appended here
]
];
foreach ($this->getAttributes() as $attribute) {
$cards['Attributes'][] = ($attribute->field_name);
}
return (new FieldLayout())->cards($cards);
}
private function getAttributes(): Collection
{
$resource = $this;
// It's good practise to cache the results of this query (at least for the current request)
return request_cache("attributes_{$resource->name()}", function() use ($resource) {
return Attribute::isActive()->forResource($resource)->get();
});
}
Manually append attributes to your fields
If you need to add attributes fields manually to your resource, you can do the following:
public function fields(): FieldCollection
{
$fields = new FieldCollection( /* your fields */ );
foreach ($this->getAttributes() as $attribute) {
$fields->add(attribute_to_field($attribute, $this->model()));
}
return $fields;
}
private function getAttributes(): Collection
{
$resource = $this;
// It's good practise to cache the results of this query (at least for the current request)
return request_cache("attributes_{$resource->name()}", function() use ($resource) {
return Attribute::isActive()->forResource($resource)->get();
});
}
The attribute_to_field
function returns an instance of the field which extends Field
, so if necessary, you can chain
any methods on it, for example:
foreach ($this->getAttributes() as $attribute) {
$fields->add(
attribute_to_field($attribute, $this->model())
->setColumnWidth(100)
);
}
Attribute type value resolving
Simple attributes like Text
and Select
will just store a string
value in the database.
However, a Checkbox
for example will cast the value to a boolean
, and a MultiSelect
will store options as json
.
You can define how values will be stored in the database by overriding the following in the attribute.php
config file:
/**
* When the value of an attribute value is accessed ($attributeValue->value),
* resolve the value based on the attribute type
*/
'attribute_value_resolver' => function(AttributeValue $attributeValue) {
$attributeType = AttributeType::tryFrom($attributeValue->attribute->type);
if ($attributeType) {
return $attributeType->resolveValue($attributeValue);
}
return $attributeValue->raw_value;
}
Using attributes outside of resources
Sometimes you can have attributes that are not linked to any resource, but still need to append them to fields.
For example, let's say we have a Test
action, and we want to append attributes here:
class Test extends Action
{
public function title(): string
{
return 'I am some action';
}
public function fields(): FieldCollection
{
return new FieldCollection(
attribute_to_field(
Attribute::where('name', 'My custom attribute')
->whereNull('resource_names')
->first()
)
);
}
public function run(Model $model, array $arguments): void
{
dd($arguments);
}
}
Because this attribute is not linked to a resource, it will throw an exception because it doesn't know which model is related to it. In order to make it work, we just need to provide a model class:
attribute_to_field(
Attribute::where('name', 'My custom attribute')
->whereNull('resource_names')
->first(),
Employee::class
)
Now, when run
is called, you should see the value of the custom attribute.
Upgrade Guide
To upgrade this module:
composer update qore/attribute
If you need to upgrade migrations, config or Vue components:
php artisan vendor:publish --tag=qore.attribute.db --force
php artisan vendor:publish --tag=qore.attribute.frontend --force
php artisan vendor:publish --tag=qore.attribute.config --force