├── .gitignore ├── DataTransformer └── EntityToIdTransformer.php ├── DependencyInjection └── GregwarFormExtension.php ├── GregwarFormBundle.php ├── LICENSE ├── README.md ├── Resources └── config │ └── services.yml ├── Tests ├── Functional │ ├── AppKernel.php │ ├── FormTest.php │ ├── Resources │ │ └── views │ │ │ └── view.html.twig │ ├── config.yml │ └── routing.yml └── bootstrap.php ├── Type └── EntityIdType.php ├── composer.json └── phpunit.xml.dist /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | -------------------------------------------------------------------------------- /DataTransformer/EntityToIdTransformer.php: -------------------------------------------------------------------------------- 1 | 20 | */ 21 | class EntityToIdTransformer implements DataTransformerInterface 22 | { 23 | protected $em; 24 | private $class; 25 | private $property; 26 | private $queryBuilder; 27 | private $multiple; 28 | 29 | private $unitOfWork; 30 | 31 | public function __construct(EntityManager $em, $class, $property, $queryBuilder, $multiple) 32 | { 33 | if (!(null === $queryBuilder || $queryBuilder instanceof QueryBuilder || $queryBuilder instanceof \Closure)) { 34 | throw new UnexpectedTypeException($queryBuilder, 'Doctrine\ORM\QueryBuilder or \Closure'); 35 | } 36 | 37 | if (null == $class) { 38 | throw new UnexpectedTypeException($class, 'string'); 39 | } 40 | 41 | $this->em = $em; 42 | $this->unitOfWork = $this->em->getUnitOfWork(); 43 | $this->class = $class; 44 | $this->queryBuilder = $queryBuilder; 45 | $this->multiple = $multiple; 46 | 47 | if ($property) { 48 | $this->property = $property; 49 | } 50 | } 51 | 52 | public function transform($data) 53 | { 54 | if (null === $data) { 55 | return null; 56 | } 57 | 58 | if (!$this->multiple) { 59 | return $this->transformSingleEntity($data); 60 | } 61 | 62 | $return = array(); 63 | 64 | foreach ($data as $element) { 65 | $return[] = $this->transformSingleEntity($element); 66 | } 67 | 68 | return implode(', ', $return); 69 | } 70 | 71 | protected function splitData($data) 72 | { 73 | return is_array($data) ? $data : explode(',', $data); 74 | } 75 | 76 | 77 | protected function transformSingleEntity($data) 78 | { 79 | if (!$this->unitOfWork->isInIdentityMap($data)) { 80 | throw new TransformationFailedException('Entities passed to the choice field must be managed'); 81 | } 82 | 83 | if ($this->property) { 84 | $propertyAccessor = new PropertyAccessor(); 85 | return $propertyAccessor->getValue($data, $this->property); 86 | } 87 | 88 | return current($this->unitOfWork->getEntityIdentifier($data)); 89 | } 90 | 91 | public function reverseTransform($data) 92 | { 93 | if (!$data) { 94 | return null; 95 | } 96 | 97 | if (!$this->multiple) { 98 | return $this->reverseTransformSingleEntity($data); 99 | } 100 | 101 | $return = array(); 102 | 103 | foreach ($this->splitData($data) as $element) { 104 | $return[] = $this->reverseTransformSingleEntity($element); 105 | } 106 | 107 | return $return; 108 | } 109 | 110 | protected function reverseTransformSingleEntity($data) 111 | { 112 | $em = $this->em; 113 | $class = $this->class; 114 | $repository = $em->getRepository($class); 115 | 116 | if ($qb = $this->queryBuilder) { 117 | if ($qb instanceof \Closure) { 118 | $qb = $qb($repository, $data); 119 | } 120 | 121 | try { 122 | $result = $qb->getQuery()->getSingleResult(); 123 | } catch (NoResultException $e) { 124 | $result = null; 125 | } 126 | } else { 127 | if ($this->property) { 128 | $result = $repository->findOneBy(array($this->property => $data)); 129 | } else { 130 | $result = $repository->find($data); 131 | } 132 | } 133 | 134 | if (!$result) { 135 | throw new TransformationFailedException('Can not find entity'); 136 | } 137 | 138 | return $result; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /DependencyInjection/GregwarFormExtension.php: -------------------------------------------------------------------------------- 1 | load('services.yml'); 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /GregwarFormBundle.php: -------------------------------------------------------------------------------- 1 | Grégoire Passault 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is furnished 8 | to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Gregwar's FormBundle 2 | ===================== 3 | 4 | [![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=YUXRLWHQSWS6L) 5 | 6 | `GregwarFormBundle` provides the form type "entity_id" 7 | 8 | Installation 9 | ============ 10 | 11 | To install `GregwarFormBundle`, run `composer require gregwar/form-bundle`. 12 | 13 | Then, register the bundle in the application kernel : 14 | 15 | ```php 16 | add('city', EntityIdType::class, array( 48 | 'class' => 'Project\Entity\City', 49 | 'query_builder' => function(EntityRepository $repo, $id) { 50 | return $repo->createQueryBuilder('c') 51 | ->where('c.id = :id AND c.available = 1') 52 | ->setParameter('id', $id); 53 | } 54 | )) 55 | ; 56 | ``` 57 | 58 | Note that if you don't provide any query builder, `->find($id)` will be used. 59 | 60 | You can also chose to show the field, by passing the `hidden` option to `false`: 61 | 62 | ```php 63 | add('city', EntityIdType::class, array( 67 | 'class' => 'Project\Entity\City', 68 | 'hidden' => false, 69 | 'label' => 'Enter the City id' 70 | )) 71 | ; 72 | ``` 73 | 74 | Using the `property` option, you can also use another identifier than the primary 75 | key: 76 | 77 | ```php 78 | add('recipient', EntityIdType::class, array( 82 | 'class' => 'Project\Entity\User', 83 | 'hidden' => false, 84 | 'property' => 'login', 85 | 'label' => 'Recipient login' 86 | )) 87 | ; 88 | ``` 89 | 90 | Notes 91 | ===== 92 | 93 | There is maybe bugs in this implementations, this package is just an idea of a form 94 | field type which can be very useful for the Symfony2 project. 95 | 96 | License 97 | ======= 98 | 99 | This bundle is under MIT license 100 | -------------------------------------------------------------------------------- /Resources/config/services.yml: -------------------------------------------------------------------------------- 1 | 2 | services: 3 | # entity_id type 4 | entity_id.type: 5 | class: Gregwar\FormBundle\Type\EntityIdType 6 | arguments: ["@doctrine"] 7 | tags: 8 | - { name: form.type, alias: entity_id } 9 | -------------------------------------------------------------------------------- /Tests/Functional/AppKernel.php: -------------------------------------------------------------------------------- 1 | load(__DIR__.'/config.yml'); 24 | } 25 | 26 | public function getCacheDir() 27 | { 28 | return sys_get_temp_dir().'/GregwarFormBundle'; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Tests/Functional/FormTest.php: -------------------------------------------------------------------------------- 1 | createKernel(); 22 | $kernel->boot(); 23 | 24 | $formBuilder = $kernel->getContainer()->get('form.factory')->createBuilder(FormType::class); 25 | $formBuilder->add('user', EntityIdType::class, array( 26 | 'class' => 'Gregwar\FormBundle\Tests\Functional\User', 27 | 'hidden' => $hidden, 28 | )); 29 | $form = $formBuilder->getForm(); 30 | 31 | $html = $kernel->getContainer()->get('twig')->render('::view.html.twig', array( 32 | 'form' => $form->createView(), 33 | )); 34 | 35 | $this->assertEquals('', trim($html)); 36 | } 37 | 38 | /** 39 | * @expectedException Symfony\Component\OptionsResolver\Exception\MissingOptionsException 40 | * @expectedExceptionMessage The required option "class" is missing. 41 | */ 42 | public function testFormWithNoClass() 43 | { 44 | $kernel = $this->createKernel(); 45 | $kernel->boot(); 46 | 47 | $formBuilder = $kernel->getContainer()->get('form.factory')->createBuilder(FormType::class); 48 | $formBuilder->add('user', EntityIdType::class); 49 | $form = $formBuilder->getForm(); 50 | } 51 | 52 | public function getTestFormData() 53 | { 54 | return array( 55 | array(true, 'hidden'), 56 | array(false, 'text') 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Tests/Functional/Resources/views/view.html.twig: -------------------------------------------------------------------------------- 1 | {{ form_widget(form.user) }} 2 | -------------------------------------------------------------------------------- /Tests/Functional/config.yml: -------------------------------------------------------------------------------- 1 | framework: 2 | secret: test 3 | test: ~ 4 | form: true 5 | templating: 6 | engines: ['twig'] 7 | router: 8 | resource: "%kernel.root_dir%/config/routing.yml" 9 | strict_requirements: "%kernel.debug%" 10 | session: 11 | storage_id: session.storage.mock_file 12 | 13 | doctrine: 14 | dbal: 15 | driver: pdo_sqlite 16 | path: "%kernel.cache_dir%/data.sqlite" 17 | orm: 18 | auto_generate_proxy_classes: "%kernel.debug%" 19 | entity_managers: 20 | default: 21 | auto_mapping: true 22 | -------------------------------------------------------------------------------- /Tests/Functional/routing.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Gregwar/FormBundle/b84c52fcbf8c9d3caac55a30c04696297e7413d0/Tests/Functional/routing.yml -------------------------------------------------------------------------------- /Tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | class EntityIdType extends AbstractType 21 | { 22 | protected $registry; 23 | 24 | public function __construct(RegistryInterface $registry) 25 | { 26 | $this->registry = $registry; 27 | } 28 | 29 | public function buildForm(FormBuilderInterface $builder, array $options) 30 | { 31 | $builder->addModelTransformer(new EntityToIdTransformer( 32 | $this->registry->getManager($options['em']), 33 | $options['class'], 34 | $options['property'], 35 | $options['query_builder'], 36 | $options['multiple'] 37 | )); 38 | } 39 | 40 | // Todo: remove when Symfony < 2.7 support is dropped 41 | public function setDefaultOptions(OptionsResolverInterface $resolver) 42 | { 43 | $this->configureOptions($resolver); 44 | } 45 | 46 | public function configureOptions(OptionsResolver $resolver) 47 | { 48 | $resolver->setRequired(array( 49 | 'class', 50 | )); 51 | 52 | $resolver->setDefaults(array( 53 | 'em' => null, 54 | 'property' => null, 55 | 'query_builder' => null, 56 | 'hidden' => true, 57 | 'multiple' => false, 58 | )); 59 | } 60 | 61 | public function buildView(FormView $view, FormInterface $form, array $options) 62 | { 63 | if (true === $options['hidden']) { 64 | $view->vars['type'] = 'hidden'; 65 | } 66 | } 67 | 68 | public function getParent() 69 | { 70 | // BC for SF < 2.8 71 | if (!method_exists('Symfony\Component\Form\AbstractType', 'getBlockPrefix')) { 72 | return 'text'; 73 | } 74 | 75 | return 'Symfony\Component\Form\Extension\Core\Type\TextType'; 76 | } 77 | 78 | public function getBlockPrefix() 79 | { 80 | return 'entity_id'; 81 | } 82 | 83 | // BC for SF < 2.8 84 | public function getName() 85 | { 86 | return $this->getBlockPrefix(); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gregwar/form-bundle", 3 | "description": "Provides the \"entity_id\" type (read \"entity identifier\")", 4 | "license": "MIT", 5 | "require": { 6 | "symfony/framework-bundle": "^2.8 || ^3.0 || ^4.0", 7 | "symfony/form": "^2.8 || ^3.0 || ^4.0", 8 | "symfony/validator": "^2.8 || ^3.0 || ^4.0", 9 | "symfony/property-access": "^2.8 || ^3.0 || ^4.0", 10 | "doctrine/orm": "^2.2" 11 | }, 12 | "require-dev": { 13 | "doctrine/doctrine-bundle": "^1.3", 14 | "symfony/twig-bundle": "^2.8 || ^3.0 || ^4.0", 15 | "symfony/twig-bridge": "^2.8 || ^3.0 || ^4.0", 16 | "symfony/yaml": "^2.8 || ^3.0 || ^4.0", 17 | "symfony/templating": "^2.8 || ^3.0 || ^4.0" 18 | }, 19 | "autoload": { 20 | "psr-0": { "Gregwar\\FormBundle": "" } 21 | }, 22 | "target-dir": "Gregwar/FormBundle", 23 | "extra": { 24 | "branch-alias": { 25 | "dev-master": "2.6-dev" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 15 | ./Tests/ 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------