Skip to main content

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
],
success

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
]
],
info

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,
info

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:

use Qore\System\Resource\Action\Action;

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