2

I have a problem I have been facing for days now. I want to override the magento the city (in the admin order create) to be a dropdown list of cities. This has already been done in the Checkout for users. I also want it to in the backend when creating orders.

I am unable to do so. Have searched through the internet for resources but found none.

I have checked the layout for the page ans seen the template responsible for rendering. But the form is generated by magento. So I tried creating a plugin to Magento\Sales\Block\Adminhtml\Order\Create\Form\Address on the getForm method to modify the form elements. But unfortunately the $_form property is protected so I cannot access it from outside the class. So that is blocked. I don't know how else to go about it. Any piece of code or help will be grateful

Here is an image of the order create form. enter image description here

1
  • Are you talking about State? I haven't seen city as dropdown in Magento since Magento 1
    – Jimmy
    Commented Mar 18, 2021 at 2:06

1 Answer 1

5

Alright. I will answer this question to help any other future person who may be facing this same issue.

To begin, we will check for the layout that is responsible for producing the admin order create view. This file is found in magento_root/vendor/magento/module-sales/view/adminhtml/layout/sales_order_create_index.xml In this file, you will find that the template responsible for generating the address form an order (Shipping and Billing address), which is <block class="Magento\Sales\Block\Adminhtml\Order\Create\Shipping\Address" template="Magento_Sales::order/create/form/address.phtml" name="shipping_address"> for Shipping and something similar for billing address. But ultimately the same template file.

Therefore the template responsible for showing the user address form in the order create form is magento_root/vendor/magento/module-sales/view/adminhtml/templates/order/create/form/address.phtml.

In this file the code <?= $block->getForm()->toHtml() ?> is reponsible for outputing the html form. So in order to achieve what we want, we will need to be able to modify the result of getForm before converting it to html.

The block that is responsible for this template is Magento\Sales\Block\Adminhtml\Order\Create\Form\Address.php which is located in magento_root/vendor/magento/module-sales/Block/Adminhtml/Order/Create/Form/Address.php. This class extends the AbstractForm which is still in the same folder.

The AbstractForm class has the method getForm() which has the code below

public function getForm()
    {
        if ($this->_form === null) {
            $this->_form = $this->_formFactory->create();
            $this->_prepareForm();

        }

        return $this->_form;
    }

It checks for the form and and creates an instance of a form and prepares the form. The _prepareForm() is a method implemented by the class Address which is the block we saw earlier.

So in the preparation of the form, the most important lines of code are these ones

$fieldset = $this->_form->addFieldset('main', ['no_container' => true]);

        $addressForm = $this->_customerFormFactory->create('customer_address', 'adminhtml_customer_address');
        $attributes = $addressForm->getAttributes();

        $this->_addAttributesToForm($attributes, $fieldset);

The most important line is when we are getting the attributes. The attributes are classes resposible for the declaring the input type for the an input, setting its class, label, etc.

So in order to achieve a dropdown list for city, a plugin has to be created which will modify the output $addressForm->getAttributes() so that the attributes are exactly what we want before adding them to the form.

I will not be explaining how to create a module here, I assume if you want to do this, then you already know the basics of creating a magento module.

In your module etc/di.xml file add the line

<type name="Magento\Customer\Model\Metadata\Form">
        <plugin name="modify_create_order_fields"
                type="Vendor\Module\Plugin\ModifyCreateAddressFields"/>
    </type>

And in your Vendor\Module\Plugin\ModifyCreateAddressFields you will then modify the attributes as you want.

NB: Vendor\Module refers to your module

Attributes are instances of Magento\Customer\Model\Data\AttributeMetadata. So you can refer to the class to see the methods that you have access to.

Here is a sample code for me to modify the city attribute to display a drowpdown list instead if an input.

<?php
namespace Vendor\Module\Plugin;

use Vendor\Module\Model\TownFactory;

class ModifyCreateAddressFields
{
    protected $townFactory;


    public function __construct(
        TownFactory $townFactory
    )
    {
        $this->townFactory = $townFactory;
    }
    public function afterGetAttributes($subject, $result)
    {
        if(isset($result['city']))
        {
            $city = $result['city'];
            $this->setupCity($city);

        }
        
        return $result;
    }

    private function setupCity($element)
    {
        $element->setFrontendInput('select');

        $options = $this->getTownsCollection();

        $element->setOptions($options);
        
    }

    private function getTownsCollection()
    {
        $townModel = $this->townFactory->create();

        $townsCollection = $townModel->getCollection()->setOrder('name', 'ASC');

        $options = [];

        foreach($townsCollection as $item)
        {
            $name = $item->getData("name");

            $option = new \Magento\Customer\Model\Data\Option;
            $option->setLabel($name);
            $option->setValue($name);

            array_push($options, $option);
        }

        return $options;
    }

}

I hope this helps. And if someone finds a better way I would like to know.

1
  • Thanks for the answer. It's well explained. Do you have any idea, how we can filter city values based on the region selected? Commented Dec 29, 2021 at 9:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.