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:
bundles: - { name: Ibnab\Bundle\ExtensionFieldBundle\IbnabExtensionFieldBundle , priority: 8002 }
Then create your IbnabExtensionFieldBundle.php inside src/ibnab/Bundle/ ExtensionFieldBundle/ and fill with:
<?php namespace Ibnab\Bundle\ExtensionFieldBundle; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; class IbnabExtensionFieldBundle extends Bundle { public function getParent() { return 'OroCustomerBundle'; } /** * {@inheritdoc} */ public function build(ContainerBuilder $container) { parent::build($container); } }
So with:
public function getParent() { return 'OroCustomerBundle'; }
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:
<?php namespace Ibnab\Bundle\ExtensionFieldBundle\Migrations\Schema\v1_0; use Doctrine\DBAL\Schema\Schema; use Oro\Bundle\EntityExtendBundle\EntityConfig\ExtendScope; use Oro\Bundle\MigrationBundle\Migration\Migration; use Oro\Bundle\MigrationBundle\Migration\QueryBag; /** * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.ExcessiveClassLength) */ class IbnabExtensionFieldBundle implements Migration { /** * {@inheritdoc} */ public function up(Schema $schema, QueryBag $queries) { $table = $schema->getTable('oro_customer_user'); $table->addColumn('type', 'string', [ 'oro_options' => [ 'extend' => ['owner' => ExtendScope::OWNER_CUSTOM], ], ]); } }
'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 :
<?php namespace Ibnab\Bundle\ExtensionFieldBundle\DependencyInjection; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; class Configuration implements ConfigurationInterface { /** * {@inheritDoc} */ public function getConfigTreeBuilder() { $treeBuilder = new TreeBuilder(); //$rootNode = $treeBuilder->root('ibnab_extension_field'); return $treeBuilder; } }
content of IbnabExtensionFieldExtension.php :
<?php namespace Ibnab\Bundle\ExtensionFieldBundle\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; use Symfony\Component\HttpKernel\DependencyInjection\Extension; use Symfony\Component\DependencyInjection\Loader;</p> <p>class IbnabExtensionFieldExtension extends Extension { /** * {@inheritDoc} */ { $configuration = new Configuration(); $config = $this->processConfiguration($configuration, $configs); $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); $loader->load('form.yml'); $container->prependExtensionConfig($this->getAlias(), array_intersect_key($config, array_flip(['settings']))); } /** * Get alias * * @return string */ public function getAlias() { return 'ibnab_extension_field'; } }
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:
parameters: ibnab_extension_field.extension.customer_type_extension.class: Ibnab\Bundle\ExtensionFieldBundle\Form\Extension\CustumerTypeExtension ibnab_extension.form.type.extensionfield_select.class: Ibnab\Bundle\ExtensionFieldBundle\Form\Type\CustomerTypeSelectType ibnab_extension_field.extension.frontend_customer_type_extension.class: Ibnab\Bundle\ExtensionFieldBundle\Form\Extension\FrontendCustumerTypeExtension services: ibnab_extension_field.extension.customer_type_extension: class: %ibnab_extension_field.extension.customer_type_extension.class% tags: - { name: form.type_extension, extended_type: 'Oro\Bundle\CustomerBundle\Form\Type\CustomerUserType' } ibnab_extension_field.extension.frontned_customer_type_extension: class: %ibnab_extension_field.extension.frontend_customer_type_extension.class% tags: - { name: form.type_extension, extended_type: 'Oro\Bundle\CustomerBundle\Form\Type\FrontendCustomerUserRegistrationType' } ibnab_extension.form.type.extensionfield_select: class: '%ibnab_extension.form.type.extensionfield_select.class%' tags: - { name: form.type, alias: "ibnab_extensionfield_select" }
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 :
<?php namespace Ibnab\Bundle\ExtensionFieldBundle\Form\Type; use Oro\Bundle\FormBundle\Form\Type\Select2ChoiceType; use Symfony\Component\Form\AbstractType; use Symfony\Component\OptionsResolver\OptionsResolver; class CustomerTypeSelectType extends AbstractType { /** * {@inheritdoc} */ public function configureOptions(OptionsResolver $resolver) { parent::configureOptions($resolver); $resolver->setDefaults( [ 'choices' => ['buyer' => 'Buyer' , 'seller' => 'Seller' ], 'label' => 'oro.customer.customeruser.type.label' ] ); } /** * {@inheritdoc} */ public function getParent() { return Select2ChoiceType::class; } /** * {@inheritdoc} */ public function getName() { return $this->getBlockPrefix(); } /** * {@inheritdoc} */ public function getBlockPrefix() { return 'ibnab_extensionfield_select'; } }
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 :
<?php namespace Ibnab\Bundle\ExtensionFieldBundle\Form\Extension; use Oro\Bundle\CustomerBundle\Form\Type\CustomerUserType; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Ibnab\Bundle\ExtensionFieldBundle\Form\Type\CustomerTypeSelectType; class CustumerTypeExtension extends AbstractTypeExtension { $builder ->add('type', CustomerTypeSelectType::class, ['mapped' => false, 'label' => 'oro.customer.customeruser.type.label']) ->addEventListener( FormEvents::POST_SUBMIT, function (FormEvent $event) { $customerUser = $event->getData(); $type = $event->getForm()->get('type')->getData(); }, 5 )->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) { $event->getForm()->add('type', CustomerTypeSelectType::class, ['mapped' => false,'data' => $event->getData()->getType(),'label' => 'Type']); } }); } public function getExtendedType() { return CustomerUserType::class; } }
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:
<?php namespace Ibnab\Bundle\ExtensionFieldBundle\Form\Extension; use Oro\Bundle\CustomerBundle\Form\Type\FrontendCustomerUserRegistrationType; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Ibnab\Bundle\ExtensionFieldBundle\Form\Type\CustomerTypeSelectType; class FrontendCustumerTypeExtension extends AbstractTypeExtension { /** * {@inheritDoc} */ $builder ->add('type', CustomerTypeSelectType::class, ['mapped' => false, 'label' => 'oro.customer.customeruser.type.label']) ->addEventListener( FormEvents::POST_SUBMIT, function (FormEvent $event) { $customerUser = $event->getData(); $type = $event->getForm()->get('type')->getData(); }, 5 )->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) { $event->getForm()->add('type', CustomerTypeSelectType::class, ['mapped' => false,'data' => $event->getData()->getType(),'label' => 'Type']); } }); } /** * {@inheritDoc} */ public function getExtendedType() { return FrontendCustomerUserRegistrationType::class; } }
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