Custom fields
Use your own custom/meta fields
Kimai supports custom fields for the following object types:
User
viaUserPreference
(see user preferences)Timesheet
viaTimesheetMeta
Customer
viaCustomerMeta
Project
viaProjectMeta
Activity
viaActivityMeta
Invoice
viaInvoiceMeta
Below you find the documentation, how a developer can create custom fields.
Most users will prefer to use the Custom fields plugin.
Custom fields
Using the fields for internal reasons (eg. importing and linking to IDs of external apps) is simple. You can add these fields programmatically at any time:
$externalId = (new TimesheetMeta())->setName('externalID')->setValue(1);
$timesheet = new Timesheet();
$timesheet->setMetaField($externalId);
But what if you want the field to be editable by users?
Well, this is possible through the registration via an EventSubscriber, where you add your custom fields.
Add editable custom fields
The following example adds a custom field to each entity type, which can be edited in the “edit” and “create” forms:
Timesheet
viaTimesheetMeta
Customer
viaCustomerMeta
Project
viaProjectMeta
Activity
viaActivityMeta
This example supports all possible entity types and adds the new Location
field.
See in prepareEntity()
what needs to be done to setup new custom fields, which can be edited by the user.
use App\Entity\ActivityMeta;
use App\Entity\CustomerMeta;
use App\Entity\EntityWithMetaFields;
use App\Entity\InvoiceMeta;
use App\Entity\MetaTableTypeInterface;
use App\Entity\ProjectMeta;
use App\Entity\TimesheetMeta;
use App\Event\ActivityMetaDefinitionEvent;
use App\Event\CustomerMetaDefinitionEvent;
use App\Event\InvoiceMetaDefinitionEvent;
use App\Event\ProjectMetaDefinitionEvent;
use App\Event\TimesheetMetaDefinitionEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\Length;
class MetaFieldSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
TimesheetMetaDefinitionEvent::class => ['loadTimesheetMeta', 200],
CustomerMetaDefinitionEvent::class => ['loadCustomerMeta', 200],
ProjectMetaDefinitionEvent::class => ['loadProjectMeta', 200],
ActivityMetaDefinitionEvent::class => ['loadActivityMeta', 200],
InvoiceMetaDefinitionEvent::class => ['loadInvoiceMeta', 200],
];
}
public function loadTimesheetMeta(TimesheetMetaDefinitionEvent $event): void
{
$this->prepareEntity($event->getEntity(), new TimesheetMeta());
}
public function loadCustomerMeta(CustomerMetaDefinitionEvent $event): void
{
$this->prepareEntity($event->getEntity(), new CustomerMeta());
}
public function loadProjectMeta(ProjectMetaDefinitionEvent $event): void
{
$this->prepareEntity($event->getEntity(), new ProjectMeta());
}
public function loadActivityMeta(ActivityMetaDefinitionEvent $event): void
{
$this->prepareEntity($event->getEntity(), new ActivityMeta());
}
public function loadInvoiceMeta(InvoiceMetaDefinitionEvent $event): void
{
$this->prepareEntity($event->getEntity(), new InvoiceMeta());
}
private function prepareEntity(EntityWithMetaFields $entity, MetaTableTypeInterface $definition): void
{
$definition->setLabel('Working place');
$definition->setOptions(['help' => 'Enter the place you work from here']);
$definition->setName('location');
$definition->setType(TextType::class);
$definition->addConstraint(new Length(['max' => 255]));
$definition->setIsVisible(true);
$entity->setMetaField($definition);
}
}
Display and export custom fields
Custom-fields can be shown as new columns in the data-tables for timesheets, customers, projects and activities. Additionally, these fields will be added to HTML and Spreadsheet exports.
As Kimai cannot query all existing records for possible custom fields, you need to listen to new events and register the desired fields.
use App\Entity\ActivityMeta;
use App\Entity\CustomerMeta;
use App\Entity\EntityWithMetaFields;
use App\Entity\MetaTableTypeInterface;
use App\Entity\ProjectMeta;
use App\Entity\TimesheetMeta;
use App\Event\ActivityMetaDisplayEvent;
use App\Event\CustomerMetaDisplayEvent;
use App\Event\ProjectMetaDisplayEvent;
use App\Event\TimesheetMetaDisplayEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Validator\Constraints\Length;
class MetaFieldDisplaySubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
TimesheetMetaDisplayEvent::class => ['loadTimesheetField', 200],
CustomerMetaDisplayEvent::class => ['loadCustomerField', 200],
ProjectMetaDisplayEvent::class => ['loadProjectField', 200],
ActivityMetaDisplayEvent::class => ['loadActivityField', 200],
];
}
public function loadTimesheetField(TimesheetMetaDisplayEvent $event): void
{
$event->addField($this->prepareField(new TimesheetMeta()));
}
public function loadCustomerField(CustomerMetaDisplayEvent $event): void
{
$event->addField($this->prepareField(new CustomerMeta()));
}
public function loadProjectField(ProjectMetaDisplayEvent $event): void
{
$event->addField($this->prepareField(new ProjectMeta()));
}
public function loadActivityField(ActivityMetaDisplayEvent $event): void
{
$event->addField($this->prepareField(new ActivityMeta()));
}
private function prepareField(MetaTableTypeInterface $definition): MetaTableTypeInterface
{
$definition->setLabel('Working place');
$definition->setName('location');
$definition->setType(TextType::class);
return $definition;
}
}
Visibility
Each meta field has its own visibility, which determines whether the field will be exposed in the following places:
- Export
- API
- Datatables
The default visibility is false
(hidden). If you want to show the meta fields in the datatables (e.g. timesheets),
then you have to set visibility
to true
(see EventSubscriber example above).
Be aware: the visibility is stored with the meta field, so changing its value via the EventSubscriber does NOT change the visibility of already saved meta fields, just for new ones.