## Getting Started
### Minimal Setup for a Custom Entity
1. **Install the Bundle**
```bash
composer require akeneo-labs/custom-entity-bundle
Enable in config/bundles.php:
Pim\Bundle\CustomEntityBundle\PimCustomEntityBundle::class => ['all' => true]
Define Your Entity Class
Extend AbstractCustomEntity and add your fields:
// src/Acme/SupplierBundle/Entity/Supplier.php
namespace Acme\Bundle\SupplierBundle\Entity;
use Pim\Bundle\CustomEntityBundle\Entity\AbstractCustomEntity;
class Supplier extends AbstractCustomEntity
{
protected $name;
public function getName(): string { return $this->name; }
public function setName(string $name): void { $this->name = $name; }
}
Configure Doctrine Mapping
# src/Acme/SupplierBundle/Resources/config/doctrine/Supplier.orm.yml
Acme\Bundle\SupplierBundle\Entity\Supplier:
type: entity
table: refdata_supplier
repositoryClass: Pim\Bundle\CustomEntityBundle\Entity\Repository\CustomEntityRepository
fields:
id: ~
code: ~
name:
type: string
length: 255
Register the Entity
# src/Acme/SupplierBundle/Resources/config/custom_entities.yml
custom_entities:
supplier:
entity_class: Acme\Bundle\SupplierBundle\Entity\Supplier
Create Database Table
php bin/console doctrine:schema:update --force
Add Menu Entry
# src/Acme/SupplierBundle/Resources/config/form_extensions/menu.yml
extensions:
acme-menu-supplier:
module: pim/menu/item
parent: pim-menu-reference_data-navigation-block
config:
title: acme.menu.supplier
to: pim_customentity_index
routeParams:
customEntityName: supplier
Configure Datagrid
# src/Acme/SupplierBundle/Resources/config/datagrid/supplier.yml
datagrid:
supplier:
options:
entityHint: supplier
source:
type: pim_datasource_default
entity: Acme\Bundle\SupplierBundle\Entity\Supplier
repository_method: createDatagridQueryBuilder
columns:
code: ~
name: ~
Clear Cache & Rebuild Assets
php bin/console cache:clear
yarn run webpack-dev
name field.Entity Definition
AbstractCustomEntity for all custom entities.code (unique identifier) and id (auto-incremented primary key) as mandatory fields.class Brand extends AbstractCustomEntity {
protected $logoUrl;
protected $isActive;
// Getters/setters...
}
Doctrine Configuration
refdata_brand).repositoryClass: Pim\Bundle\CustomEntityBundle\Entity\Repository\CustomEntityRepository.UI Integration
pim-menu-reference_data-navigation-block to add a menu item.datagrid/{entity}.yml.
columns:
code: ~
name:
type: pim_datagrid_field_string
label: acme.brand.name
is_active:
type: pim_datagrid_field_boolean
label: acme.brand.is_active
create.yml and edit.yml for CRUD operations.
# src/Acme/BrandBundle/Resources/config/form_extensions/brand/create.yml
acme-brand-create-form:
module: pim/form/standard
parent: pim-entity-create-form
targetZone: content
position: 100
config:
fields:
code:
type: pim_form_field_text
options:
label: acme.brand.code
required: true
name:
type: pim_form_field_text
options:
label: acme.brand.name
required: true
Validation
# src/Acme/BrandBundle/Resources/config/validation.yml
Acme\Bundle\BrandBundle\Entity\Brand:
constraints:
- Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity:
fields: code
message: acme.brand.code.unique
properties:
name:
- NotBlank: ~
- Length:
max: 255
Relationships
# Link a product to a supplier (single select)
acme-product-edit-form-properties-supplier:
module: custom_entity/field/custom-entity-select
parent: pim-entity-edit-form-properties-common
targetZone: content
position: 200
config:
fieldName: supplier
choiceNameField: name
choiceValueField: code
isCustomEntity: true
entityName: supplier
required: false
Normalization
use Pim\Bundle\CustomEntityBundle\Normalizer\CustomEntityNormalizerInterface;
class BrandNormalizer implements CustomEntityNormalizerInterface {
public function normalize($entity, $format = null, array $context = []) {
return [
'code' => $entity->getCode(),
'name' => $entity->getName(),
'is_active' => $entity->isActive(),
];
}
}
services.yml:
services:
acme.brand.normalizer:
class: Acme\Bundle\BrandBundle\Normalizer\BrandNormalizer
tags:
- { name: pim_custom_entity.normalizer, entity: brand }
Batch Operations
php bin/console akeneo:batch:create-job "Supplier Export" "csv_supplier_export" "export" "csv_supplier_export" '{"filePath": "/tmp/suppliers.csv"}'
Cache Invalidation
php bin/console cache:clear) after updating YAML configs (e.g., datagrid, form_extensions, menu).php bin/console cache:clear --env=prod
yarn run webpack-dev
Doctrine Schema Updates
--force without checking --dump-sql first can break existing data.php bin/console doctrine:schema:update --dump-sql
Entity Naming Conflicts
product, family) as custom entity names can cause routing issues.acme_brand, vendor_supplier).Form Extension Target Zones
targetZone in form extensions (e.g., content vs. header) can hide fields.pim-entity-edit-form).Missing Normalizers
/api/rest/v1/supplier) will return null if no normalizer is registered.CustomEntityNormalizerInterface for each entity.Multi-Select Fields
isMultiple: true in the form component config.config:
isMultiple: true
fieldName: fabrics
entityName: fabric
Translation Keys
acme.brand.name).translations/messages.{locale}.yml:
acme:
brand:
name: "Brand Name"
code: "Brand Code"
How can I help you explore Laravel packages today?