Custom Choice Field Type - adamwojs/ezplatform-fieldtype-library GitHub Wiki
ezplatform-fieldtype-library
provides abstract implementation of choice field type.
Example #1: Enumerated values
The following example shows how to build the U.S. State choice field type. Implementation assumes that U.S. State will be a Value Object and list of all available choices will be stored in-memory.
1. Create Value Object representing the state
Create the Value Object representing the U.S. State with two properties: the name and code of the state.
<?php
declare(strict_types=1);
namespace App\FieldType\State;
final class State
{
/** @var string */
private $name;
/** @var string */
private $code;
public function __construct(string $name, string $code)
{
$this->name = $name;
$this->code = $code;
}
public function getName(): string
{
return $this->name;
}
public function getCode(): string
{
return $this->code;
}
public function __toString()
{
return $this->name;
}
}
2. Create Field Type Definition
<?php
declare(strict_types=1);
namespace App\FieldType\State;
use AdamWojs\EzPlatformFieldTypeLibrary\Core\FieldType\AbstractChoice\Type as AbstractChoiceType;
final class Type extends AbstractChoiceType
{
public function getFieldTypeIdentifier(): string
{
return 'state';
}
}
ChoiceProvider
implementation
3. Provide List of the U.S. States (represented as State Value Object) is pretty small so in this example we will keep it in the memory.
<?php
declare(strict_types=1);
namespace App\FieldType\State;
use AdamWojs\EzPlatformFieldTypeLibrary\API\FieldType\AbstractChoice\ChoiceProvider as ChoiceProviderInterface;
use AdamWojs\EzPlatformFieldTypeLibrary\API\FieldType\AbstractChoice\ChoiceCriteria;
use AdamWojs\EzPlatformFieldTypeLibrary\API\FieldType\AbstractChoice\ChoiceList;
final class ChoiceProvider implements ChoiceProviderInterface
{
/** @var \App\FieldType\State\State[] */
private $states = [];
public function __construct()
{
// Initialize states list
foreach ($this->getAvailableStates() as $code => $name) {
$this->states[] = new State($name, $code);
}
}
public function getChoiceList(ChoiceCriteria $criteria, ?int $offset = null, ?int $limit = null): ChoiceList
{
$states = $this->states;
if ($criteria->hasValues()) {
$states = array_filter($this->states, static function (State $state) use($criteria) {
return in_array($state->getCode(), $criteria->getValues());
});
}
return new ChoiceList($states, count($states));
}
public function getValueForChoice($choice): string
{
/** @var \App\FieldType\State\State $choice */
return $choice->getCode();
}
public function getLabelForChoice($choice): string
{
/** @var \App\FieldType\State\State $choice */
return $choice->getName();
}
private function getAvailableStates(): array
{
return [
'AL' => 'Alabama',
'AK' => 'Alaska',
'AZ' => 'Arizona',
'AR' => 'Arkansas',
'CA' => 'California',
'CO' => 'Colorado',
'CT' => 'Connecticut',
'DE' => 'Delaware',
'FL' => 'Florida',
'GA' => 'Georgia',
'HI' => 'Hawaii',
'ID' => 'Idaho',
// ...
];
}
}
4. Configure services
services:
_defaults:
autowire: true
autoconfigure: true
public: false
app.field_type.state:
class: App\FieldType\State\Type
arguments:
$choiceProvider: '@app.field_type.state.choice_provider'
tags:
- { name: ezplatform.field_type, alias: state }
app.field_type.state.choice_provider:
class: App\FieldType\State\ChoiceProvider
app.field_type.state.converter:
class: AdamWojs\EzPlatformFieldTypeLibrary\Core\Persistence\Legacy\Converter\ChoiceConverter
tags:
- { name: ezplatform.field_type.legacy_storage.converter, alias: state }
app.field_type.state.indexable:
class: AdamWojs\EzPlatformFieldTypeLibrary\Core\FieldType\AbstractChoice\SearchField
tags:
- { name: ezplatform.field_type.indexable, alias: state }
app.field_type.state.form_mapper.value:
class: AdamWojs\EzPlatformFieldTypeLibrary\Core\FieldType\AbstractChoice\FormMapper\FieldValueFormMapper
arguments:
$choiceProvider: '@app.field_type.state.choice_provider'
tags:
- { name: ezplatform.field_type.form_mapper.value, fieldType: state }
app.field_type.state.form_mapper.definition:
class: AdamWojs\EzPlatformFieldTypeLibrary\Core\FieldType\AbstractChoice\FormMapper\FieldDefinitionFormMapper
tags:
- { name: ezplatform.field_type.form_mapper.definition, fieldType: state }
Example 2: Doctrine Entity
The following example shows how to build the Product Category choice field type.
1. Create Entity representing the product category
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class ProductCategory
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private $id;
/**
* @ORM\Column(type="string", length=255)
*/
private $name;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function setName($name)
{
$this->name = $name;
}
}
2. Create Field Type Definition
<?php
declare(strict_types=1);
namespace App\FieldType\ProductCategoryChoice;
use AdamWojs\EzPlatformFieldTypeLibrary\Core\FieldType\AbstractChoice\Type as AbstractChoiceType;
final class Type extends AbstractChoiceType
{
public function getFieldTypeIdentifier(): string
{
return 'productcategorychoice';
}
}
ChoiceProvider
implementation
3. Provide <?php
declare(strict_types=1);
namespace App\FieldType\ProductCategoryChoice;
use AdamWojs\EzPlatformFieldTypeLibrary\API\FieldType\AbstractChoice\ChoiceCriteria;
use AdamWojs\EzPlatformFieldTypeLibrary\API\FieldType\AbstractChoice\ChoiceList;
use AdamWojs\EzPlatformFieldTypeLibrary\API\FieldType\AbstractChoice\ChoiceProvider as ChoiceProviderInterface;
use App\Entity\ProductCategory;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\QueryBuilder;
final class ChoiceProvider implements ChoiceProviderInterface
{
/** @var \Doctrine\ORM\EntityRepository */
private $repository;
public function __construct(EntityManagerInterface $em)
{
$this->repository = $em->getRepository(ProductCategory::class);
}
public function getChoiceList(ChoiceCriteria $criteria, ?int $offset = null, ?int $limit = null): ChoiceList
{
$totalCount = $this->countChoices($criteria);
$choices = [];
if ($totalCount !== 0) {
$choices = $this->findChoices($criteria, $offset, $limit)->toArray();
}
return new ChoiceList($choices, $totalCount);
}
public function getValueForChoice($choice): string
{
/** @var \App\Entity\ProductCategory $choice */
return $choice->getId();
}
public function getLabelForChoice($choice): string
{
/** @var \App\Entity\ProductCategory $choice */
return $choice->getName();
}
private function findChoices(ChoiceCriteria $criteria, ?int $offset = null, ?int $limit = null): Collection
{
$qb = $this->createChoiceQueryBuilder($criteria, 'c');
$qb->addOrderBy('c.name', 'DESC');
$qb->setFirstResult($offset);
$qb->setMaxResults($limit);
return $qb->getQuery()->execute();
}
private function countChoices(ChoiceCriteria $criteria): int
{
$qb = $this->createChoiceQueryBuilder($criteria, 'c');
$qb->select($qb->expr()->count('c.id'));
return (int)$qb->getQuery()->getSingleScalarResult();
}
private function createChoiceQueryBuilder(ChoiceCriteria $criteria, string $alias = 'c'): QueryBuilder
{
$qb = $this->repository->createQueryBuilder($alias);
if ($criteria->hasValues()) {
$qb->andWhere($qb->expr()->in(
$alias . '.id',
$criteria->getValues()
));
}
if ($criteria->getSearchTerm() !== null) {
$qb->andWhere($qb->expr()->like(
$alias . '.name',
$criteria->getSearchTerm() . '*'
));
}
return $qb;
}
}
4. Configure services
services:
_defaults:
autowire: true
autoconfigure: true
public: false
app.field_type.productcategorychoice:
class: App\FieldType\ProductCategoryChoice\Type
arguments:
$choiceProvider: '@app.field_type.productcategorychoice.choice_provider'
tags:
- { name: ezplatform.field_type, alias: productcategorychoice }
app.field_type.productcategorychoice.choice_provider:
class: App\FieldType\ProductCategoryChoice\ChoiceProvider
app.field_type.productcategorychoice.converter:
class: AdamWojs\EzPlatformFieldTypeLibrary\Core\Persistence\Legacy\Converter\ChoiceConverter
tags:
- { name: ezplatform.field_type.legacy_storage.converter, alias: productcategorychoice }
app.field_type.productcategorychoice.indexable:
class: AdamWojs\EzPlatformFieldTypeLibrary\Core\FieldType\AbstractChoice\SearchField
tags:
- { name: ezplatform.field_type.indexable, alias: productcategorychoice }
app.field_type.productcategorychoice.form_mapper.value:
class: AdamWojs\EzPlatformFieldTypeLibrary\Core\FieldType\AbstractChoice\FormMapper\FieldValueFormMapper
arguments:
$choiceProvider: '@app.field_type.productcategorychoice.choice_provider'
tags:
- { name: ezplatform.field_type.form_mapper.value, fieldType: productcategorychoice }
app.field_type.productcategorychoice.form_mapper.definition:
class: AdamWojs\EzPlatformFieldTypeLibrary\Core\FieldType\AbstractChoice\FormMapper\FieldDefinitionFormMapper
tags:
- { name: ezplatform.field_type.form_mapper.definition, fieldType: productcategorychoice }