ekyna/characteristics-bundle
Installation
composer require ekyna/characteristics-bundle
Add to config/bundles.php (Symfony) or config/app.php (Laravel wrapper if available):
Ekyna\CharacteristicsBundle\EkynaCharacteristicsBundle::class => ['all' => true],
Database Setup Run migrations (if provided in the bundle):
php artisan migrate
Check src/Resources/migrations/ for schema (if available).
First Use Case: Define a Characteristic
use Ekyna\CharacteristicsBundle\Entity\Characteristic;
$characteristic = new Characteristic();
$characteristic->setName('Color');
$characteristic->setSlug('color');
$em->persist($characteristic);
$em->flush();
Key Files to Review
src/Resources/config/doctrine/ (Entity mappings)src/DependencyInjection/ (Configuration logic)src/Entity/ (Core models like Characteristic, CharacteristicValue)CRUD for Characteristics
// Create
$characteristic = $this->getDoctrine()->getRepository(Characteristic::class)->create(['name' => 'Size']);
// Read
$characteristic = $this->getDoctrine()->getRepository(Characteristic::class)->findOneBy(['slug' => 'size']);
// Update
$characteristic->setDescription('Product dimensions');
$em->flush();
// Delete (soft/hard)
$characteristic->setIsActive(false); // Soft delete
// OR
$em->remove($characteristic); // Hard delete
Assigning Values to Entities
// Example: Assign "Red" to a Product
$product = $entityManager->getRepository(Product::class)->find(1);
$redValue = $entityManager->getRepository(CharacteristicValue::class)->findOneBy(['slug' => 'red']);
$product->addCharacteristicValue($redValue);
$entityManager->flush();
Filtering Entities by Characteristics
// Find all products with "Red" color
$qb = $entityManager->createQueryBuilder();
$qb->select('p')
->from(Product::class, 'p')
->join('p.characteristicValues', 'cv')
->where('cv.slug = :color')
->setParameter('color', 'red');
$products = $qb->getQuery()->getResult();
Dynamic Characteristic Selection
// Get all active characteristics for a category
$category = $entityManager->getRepository(Category::class)->find(1);
$characteristics = $category->getCharacteristics()->toArray();
kernel.events (Symfony) or Laravel’s events config.
// Example: Log characteristic changes
$dispatcher->addListener(
CharacteristicEvents::POST_UPDATE,
[$this, 'onCharacteristicUpdated']
);
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'values' => CharacteristicValueResource::collection($this->values),
];
}
// Symfony FormBuilder
$builder->add('characteristics', EntityType::class, [
'class' => Characteristic::class,
'choice_label' => 'name',
'multiple' => true,
]);
Missing Migrations
src/Resources/migrations/ or manually create tables:
CREATE TABLE characteristics (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
slug VARCHAR(255) NOT NULL UNIQUE,
is_active BOOLEAN DEFAULT true
);
php artisan doctrine:schema:update --force
Entity Relationships
Product) and CharacteristicValue. Ensure your entity has:
/**
* @ORM\ManyToMany(targetEntity="Ekyna\CharacteristicsBundle\Entity\CharacteristicValue")
*/
private $characteristicValues;
Slug Uniqueness
$existing = $em->getRepository(Characteristic::class)->findOneBy(['slug' => $request->slug]);
if ($existing && $existing->getId() !== $characteristic->getId()) {
throw new \InvalidArgumentException('Slug already exists.');
}
Performance with Large Datasets
CREATE INDEX idx_characteristic_values_entity_id ON characteristic_values (entity_id);
$product->getCharacteristicValues()->matching($criteria)->toArray();
Enable Doctrine Logging
// config/packages/dev/doctrine.yaml
doctrine:
dbal:
logging: true
profiling: true
Dump Entities
dd($characteristic->getValues()->toArray());
Check for Circular Dependencies
$characteristic->setFetchValues(false); // Disable lazy loading
Custom Value Types
CharacteristicValue to add metadata (e.g., PriceRangeValue):
class PriceRangeValue extends CharacteristicValue
{
private $minPrice;
private $maxPrice;
// Add getters/setters and Doctrine mappings
}
Validation Rules
Characteristic or CharacteristicValue:
use Symfony\Component\Validator\Constraints as Assert;
/**
* @Assert\Length(max=100)
*/
private $name;
API Versioning
class CharacteristicValueSerializer extends Serializer
{
public function serialize($value, $format, array $context = [])
{
return [
'id' => $value->getId(),
'label' => $value->getLabel(),
'v1' => $value->getSlug(), // Deprecated in v2
];
}
}
Localization
Characteristic:
use Gedmo\Translatable\Entity\TranslationInterface;
/**
* @ORM\OneToMany(targetEntity="Translation", mappedBy="object")
*/
private $translations;
services.yaml.How can I help you explore Laravel packages today?