/   /   /  OroCommerce For Developer : Customize Frontend form with form type extensions

Note:

For more extensions and themes visit our store

OroCommerce For Developer : Customize Frontend form with form type extensions


Form type extensions are incredibly powerful: they allow you to modify any existing form field types across the entire system, Or add new field to specific form with flexible way .

Ok we will try to add a form filed type to frontend registration form that means should have ability to view and edit the field from admin too.

the course bundle in github

Our Bundle -Extension- name is ExtensionFieldBundle which locate inside src/ibnab/Bundle 

First of all add  bundles.yml in src/Ibnab/Bundle/ExtensionFieldBundle/Resources/config/oro/bundles.yml and push:

  1.  
  2. bundles:
  3.     - { name: Ibnab\Bundle\ExtensionFieldBundle\IbnabExtensionFieldBundle , priority: 8002 }

Then create your IbnabExtensionFieldBundle.php inside  src/ibnab/Bundle/ ExtensionFieldBundle/ and fill with:

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\ExtensionFieldBundle;
  4. use Symfony\Component\DependencyInjection\ContainerBuilder;
  5. use Symfony\Component\HttpKernel\Bundle\Bundle;
  6. class IbnabExtensionFieldBundle extends Bundle
  7. {
  8.     public function getParent()
  9.     {
  10.         return 'OroCustomerBundle';
  11.     }
  12.     /**
  13.      * {@inheritdoc}
  14.      */
  15.     public function build(ContainerBuilder $container)
  16.     {
  17.         parent::build($container);
  18.     }
  19. }

So with:

  1.  
  2.     public function getParent()
  3.     {
  4.         return 'OroCustomerBundle';
  5.     }
  6.  

We override  the OroCommerce Bundle  OroCustomerBundle , we can personalize controller , twig  and other thing just by adding to our bundle with same name and path . More info plz explore How to Extend Existing Bundle in this link:
https://oroinc.com/b2b-ecommerce/doc/current/dev-guide/extend-and-customize/how-to-extend-existing-bundle

 Is the time of Migrations To add our form field to oro_customer_user table So add IbnabExtensionFieldBundle.php in the path src/Ibnab/Bundle/ExtensionFieldBundle/Migrations/Schema/v1_0/IbnabExtensionFieldBundle.php
the content of this php file is:

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\ExtensionFieldBundle\Migrations\Schema\v1_0;
  4. use Doctrine\DBAL\Schema\Schema;
  5. use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope;
  6. use Oro\Bundle\MigrationBundle\Migration\Migration;
  7. use Oro\Bundle\MigrationBundle\Migration\QueryBag;
  8. /**
  9.  * @SuppressWarnings(PHPMD.TooManyMethods)
  10.  * @SuppressWarnings(PHPMD.ExcessiveClassLength)
  11.  */
  12. class IbnabExtensionFieldBundle implements Migration
  13. {
  14.     /**
  15.      * {@inheritdoc}
  16.      */
  17.     public function up(Schema $schema, QueryBag $queries)
  18.     {
  19.         $table = $schema->getTable('oro_customer_user');
  20.         $table->addColumn('type', 'string', [
  21.             'oro_options' => [
  22.                 'extend' => ['owner' => ExtendScope::OWNER_CUSTOM],
  23.             ],
  24.         ]);
  25.     }
  26. }
  27.  

 'extend' => ['owner' => ExtendScope::OWNER_CUSTOM] : The system is fully responsible how the custom entities and fields are used on UI .

Then add your DependencyInjection folder inside your bundle and add the two class Configuration.php and IbnabExtensionFieldExtension.php (is general symfony declaration)
content of  Configuration.php :

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\ExtensionFieldBundle\DependencyInjection;
  4. use Symfony\Component\Config\Definition\Builder\TreeBuilder;
  5. use Symfony\Component\Config\Definition\ConfigurationInterface;
  6. class Configuration implements ConfigurationInterface
  7. {
  8.     /**
  9.      * {@inheritDoc}
  10.      */
  11.     public function getConfigTreeBuilder()
  12.     {
  13.         $treeBuilder = new TreeBuilder();
  14.         //$rootNode = $treeBuilder->root('ibnab_extension_field');
  15.         return $treeBuilder;
  16.     }   
  17. }
  18.  

content of  IbnabExtensionFieldExtension.php :
  1.  
  2. <?php
  3. namespace Ibnab\Bundle\ExtensionFieldBundle\DependencyInjection;
  4. use Symfony\Component\DependencyInjection\ContainerBuilder;
  5. use Symfony\Component\Config\FileLocator;
  6. use Symfony\Component\HttpKernel\DependencyInjection\Extension;
  7. use Symfony\Component\DependencyInjection\Loader;</p>
  8.  
  9. <p>class IbnabExtensionFieldExtension extends Extension
  10. {
  11.     /**
  12.      * {@inheritDoc}
  13.      */
  14.     public function load(array $configs, ContainerBuilder $container)
  15.     {
  16.         $configuration = new Configuration();
  17.         $config = $this->processConfiguration($configuration, $configs);
  18.         $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
  19.         $loader->load('form.yml');
  20.         $container->prependExtensionConfig($this->getAlias(), array_intersect_key($config, array_flip(['settings'])));
  21.     }
  22.     /**
  23.      * Get alias
  24.      *
  25.      * @return string
  26.      */
  27.     public function getAlias()
  28.     {
  29.         return 'ibnab_extension_field';
  30.     }
  31. }

the important part here is $loader->load('form.yml'); to load service related to this bundle.

What the next yes add  form.yml in the path
src/Ibnab/Bundle/ExtensionFieldBundle/Resources/config/form.yml and fill:

  1.  
  2. parameters:
  3.     ibnab_extension_field.extension.customer_type_extension.class: Ibnab\Bundle\ExtensionFieldBundle\Form\Extension\CustumerTypeExtension
  4.     ibnab_extension.form.type.extensionfield_select.class: Ibnab\Bundle\ExtensionFieldBundle\Form\Type\CustomerTypeSelectType
  5.     ibnab_extension_field.extension.frontend_customer_type_extension.class: Ibnab\Bundle\ExtensionFieldBundle\Form\Extension\FrontendCustumerTypeExtension
  6. services:
  7.     ibnab_extension_field.extension.customer_type_extension:
  8.         class: %ibnab_extension_field.extension.customer_type_extension.class%
  9.         tags:
  10.             - { name: form.type_extension, extended_type: 'Oro\Bundle\CustomerBundle\Form\Type\CustomerUserType' }
  11.     ibnab_extension_field.extension.frontned_customer_type_extension:
  12.         class: %ibnab_extension_field.extension.frontend_customer_type_extension.class%
  13.         tags:
  14.             - { name: form.type_extension, extended_type: 'Oro\Bundle\CustomerBundle\Form\Type\FrontendCustomerUserRegistrationType' }
  15.             
  16.     ibnab_extension.form.type.extensionfield_select:
  17.         class: '%ibnab_extension.form.type.extensionfield_select.class%'
  18.         tags:
  19.             - { name: form.type, alias: "ibnab_extensionfield_select" }
  20.  


Here three service one of it is our form type just added to create custom select form type to add to our registration frontend by from extension the service responsible is    ibnab_extension.form.type.extensionfield_select related to our backend part for extension and based on the class Ibnab\Bundle\ExtensionFieldBundle\Form\Type\CustomerTypeSelectType the content of the class is : 

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\ExtensionFieldBundle\Form\Type;
  4. use Oro\Bundle\FormBundle\Form\Type\Select2ChoiceType;
  5. use Symfony\Component\Form\AbstractType;
  6. use Symfony\Component\OptionsResolver\OptionsResolver;
  7. class CustomerTypeSelectType extends AbstractType
  8. {
  9.     /**
  10.      * {@inheritdoc}
  11.      */
  12.     public function configureOptions(OptionsResolver $resolver)
  13.     {
  14.         parent::configureOptions($resolver);
  15.         $resolver->setDefaults(
  16.             [
  17.                 'choices' => ['buyer' => 'Buyer' , 'seller' => 'Seller' ],
  18.                 'label' => 'oro.customer.customeruser.type.label'
  19.             ]
  20.         );
  21.     }
  22.     /**
  23.      * {@inheritdoc}
  24.      */
  25.     public function getParent()
  26.     {
  27.         return Select2ChoiceType::class;
  28.     }
  29.     /**
  30.      * {@inheritdoc}
  31.      */
  32.     public function getName()
  33.     {
  34.         return $this->getBlockPrefix();
  35.     }
  36.     /**
  37.      * {@inheritdoc}
  38.      */
  39.     public function getBlockPrefix()
  40.     {
  41.         return 'ibnab_extensionfield_select';
  42.     }
  43. }
  44.  

Is clear that we add a select form type with two option buyer or seller , Yes is good point to create a marketplace with OroCommerce .

Now the more important part our two extension from services
1 - ibnab_extension_field.extension.customer_type_extension is responsible of backend implementation , 
and with   - { name: form.type_extension, extended_type: 'Oro\Bundle\CustomerBundle\Form\Type\CustomerUserType' } : we set which form type to use this form extension with it .

the class is Ibnab\Bundle\ExtensionFieldBundle\Form\Extension\CustumerTypeExtension which content :

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\ExtensionFieldBundle\Form\Extension;
  4. use Oro\Bundle\CustomerBundle\Form\Type\CustomerUserType;
  5. use Symfony\Component\Form\AbstractTypeExtension;
  6. use Symfony\Component\Form\FormBuilderInterface;
  7. use Symfony\Component\Form\FormEvent;
  8. use Symfony\Component\Form\FormEvents;
  9. use Ibnab\Bundle\ExtensionFieldBundle\Form\Type\CustomerTypeSelectType;
  10. class CustumerTypeExtension extends AbstractTypeExtension {
  11.     public function buildForm(FormBuilderInterface $builder, array $options) {
  12.         $builder
  13.                 ->add('type', CustomerTypeSelectType::class, ['mapped' => false, 'label' => 'oro.customer.customeruser.type.label'])
  14.                 ->addEventListener(
  15.                         FormEvents::POST_SUBMIT, function (FormEvent $event) {
  16.                     $customerUser = $event->getData();
  17.                     $type = $event->getForm()->get('type')->getData();
  18.                     $customerUser->setType($type);
  19.                 }, 5
  20.                 )->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
  21.                            if(!is_null($event->getData())){
  22.                                $event->getForm()->add('type', CustomerTypeSelectType::class, ['mapped' => false,'data' => $event->getData()->getType(),'label' => 'Type']);
  23.                            }
  24.                 });
  25.     }
  26.     public function getExtendedType() {
  27.         return CustomerUserType::class;
  28.     }
  29. }
  30.  

Depending on your use case, you may need to override some of the following methods:
    • buildForm()
    • buildView()
    • configureOptions()
    • finishView() 

in our case we’re using buildForm() and we have added our form 
builder->add('type', CustomerTypeSelectType::class, ['mapped' => false, 'label' => 'oro.customer.customeruser.type.label']) 
mapped is false to ignore auto mapping by entity .

Ok and we have add 2 EventListener :  FormEvents::POST_SUBMIT and  FormEvents::POST_SET_DATA to set data and refresh select form type data .

2 - ibnab_extension_field.extension.frontned_customer_type_extension service to managing our from in frontend the class is Ibnab\Bundle\ExtensionFieldBundle\Form\Extension\FrontendCustumerTypeExtension and content is:

  1.  
  2. <?php
  3. namespace Ibnab\Bundle\ExtensionFieldBundle\Form\Extension;
  4. use Oro\Bundle\CustomerBundle\Form\Type\FrontendCustomerUserRegistrationType;
  5. use Symfony\Component\Form\AbstractTypeExtension;
  6. use Symfony\Component\Form\FormBuilderInterface;
  7. use Symfony\Component\Form\FormEvent;
  8. use Symfony\Component\Form\FormEvents;
  9. use Ibnab\Bundle\ExtensionFieldBundle\Form\Type\CustomerTypeSelectType;
  10. class FrontendCustumerTypeExtension extends AbstractTypeExtension {
  11.     /**
  12.      * {@inheritDoc}
  13.      */
  14.     public function buildForm(FormBuilderInterface $builder, array $options) {
  15.         $builder
  16.                 ->add('type', CustomerTypeSelectType::class, ['mapped' => false, 'label' => 'oro.customer.customeruser.type.label'])
  17.                 ->addEventListener(
  18.                         FormEvents::POST_SUBMIT, function (FormEvent $event) {
  19.                     $customerUser = $event->getData();
  20.                     $type = $event->getForm()->get('type')->getData();
  21.                     $customerUser->setType($type);
  22.                 }, 5
  23.                 )->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
  24.                            if(!is_null($event->getData())){
  25.                                $event->getForm()->add('type', CustomerTypeSelectType::class, ['mapped' => false,'data' => $event->getData()->getType(),'label' => 'Type']);
  26.                            }
  27.                 });
  28.     }
  29.     /**
  30.      * {@inheritDoc}
  31.      */
  32.     public function getExtendedType() {
  33.         return FrontendCustomerUserRegistrationType::class;
  34.     }
  35. }
  36.  

is almost the same content .


Finally for backend don’t forget to add src/Ibnab/Bundle/ExtensionFieldBundle/Resources/views/CustomerUser/update.html.twig to your bundle and push form_row(form.type), in the twig file if you wanna add to view page too in the backend add src/Ibnab/Bundle/ExtensionFieldBundle/Resources/views/CustomerUser/view.html.twig.


That is the big points how to add from extension to OroCommerce for frontend and backend with some explanation.

the Video of course :

Comments

Related Posts

make your store more efficient

Solving problems. With open source technology. Professional results. That’s what makes Ibnab your best choice

IBNAB is a company made of a group of professionals whose work is providing secure open source solutions. Our company strives for reaching magnificent results with each experience and provides professional open source solutions that cover every part of the business process.