Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
372 views
in Technique[技术] by (71.8m points)

zend framework3 - Zf3 populate select Element with data from Database

I know this topic was discussed 2 years ago. But I stuck on difficulty that I would like to solve. I would like to create a zf3 form wich contains more than two Select Element. I would like to populate them with data coming from differents repositories (options values of each Select item come from distinct repository). First all, I tried to pass the service manager (from where I can access to my repositories) in the constructor of my form but I heard that this solution is not suitable at all.

So how can I include multiples repositories in my form to populate my Select Elements ?

question from:https://stackoverflow.com/questions/65907890/zf3-populate-select-element-with-data-from-database

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Short answer:

  1. Create a class that extends the Select
  2. Create a factory for such class
  3. Add this custom element in your module configuration (module.config.php)
  4. Use this class as type for your form elements
  5. Retrieve the form through the form manager
  6. Example, for controllers, adapt the controller's factory

Detailed answer:

  1. Create a class that extends the Select, like BrandSelect
namespace MyModuleFormElement;

use LaminasFormElementSelect;

class BrandSelect extends Select {

    protected $repository;

    public function __construct($repository, $name = null, $options = []) {
        parent::__construct($name, $options);
        $this->repository = $repository;
    }

    /**
     * Initialize the element
     *
     * @return void
     */
    public function init() {
        $valueOptions = [];
        foreach ($this->repository->fetchBrands() as $brand) {
            $valueOptions[$brand->getBrandId()] = $brand->getName();
        }
        asort($valueOptions);
        $this->setValueOptions($valueOptions);
    }

}
  1. Create a factory for such class
namespace MyModuleFormElement;

use LaminasServiceManagerFactoryFactoryInterface;
use InteropContainerContainerInterface;
use MyModuleDbRepository;

class BrandSelectFactory implements FactoryInterface {

    public function __invoke(ContainerInterface $container, $requestedName, $options = null): BrandSelect {
        $repository = $container->get(Repository::class);
        return new BrandSelect($repository);
    }

}
  1. Add this custom element in your module configuration (module.config.php)
namespace MyModule;

return [
    // ..
    // Other configs
    // ..
    'form_elements' => [
        'factories' => [
            FormElementBrandSelect::class => FormElementBrandSelectFactory::class
        ]
    ]
];
  1. Use this class as type for your form elements.
    It is really important to add all elements in the init() method, otherwise it will not work. I also added the InputFilterProviderInterface.
    In this case, form doens't require any other element its constructor. If needed, you must create a factory for the form and pass all params you need. The form factory must be added in module.config.php configuration, always under the form_elements key (as did for the BrandSelect):
namespace MyModuleForm;

use LaminasFormForm;
use LaminasInputFilterInputFilterProviderInterface;

class BrandForm extends Form implements InputFilterProviderInterface {

    public function __construct($name = null, $options = []) {
        parent::__construct($name, $options);
    }

    // IT IS REALLY IMPORTANT TO ADD ELEMENTS IN INIT METHOD!
    public function init() {
        parent::init();

        $this->add([
            'name' => 'brand_id',
            'type' => ElementBrandSelect::class,
            'options' => [
                'label' => 'Brands',
            ]
        ]);
    }

    public function getInputFilterSpecification() {
        $inputFilter[] = [
            'name' => 'brand_id',
            'required' => true,
            'filters' => [
                ['name' => 'Int']
            ]
        ];
        return $inputFilter;
    }

}
  1. Retrieve the form through the form manager
    Form must be retrieved using correct manager, which isn't the service manager, but the FormElementManager.
    For example, if you need the form inside BrandController:
<?php

namespace MyModuleController;

use LaminasFormFormElementManager;
use LaminasMvcControllerAbstractActionController;
use LaminasViewModelViewModel;

class BrandController extends AbstractActionController {

    private $formManager;

    public function __construct(FormElementManager $formManager) {
        $this->formManager = $formManager;
    }

    public function addBrandAction() {
        $form = $this->formManager->get(MyModuleFormBrandForm::class);

        // Do stuff

        return new ViewModel([
            'form' => $form
        ]);
    }

}
  1. Finally, you'll have to adapt the controller's factory:
namespace MyModuleController;

use LaminasServiceManagerFactoryFactoryInterface;
use InteropContainerContainerInterface;

class BrandControllerFactory implements FactoryInterface {

    public function __invoke(ContainerInterface $container, $requestedName, $options = null): BrandController {
        $formManager = $container->get('FormElementManager');
        return new BrandController($formManager);
    }

}

which must be configured under controllers key in module.config.php:

namespace MyModule;

return [
    // ..
    // Other configs
    // ..
    'controllers' => [
        'factories' => [
            ControllerBrandController::class => ControllerBrandControllerFactory::class
        ],
    ],
    'form_elements' => [
        'factories' => [
            FormElementBrandSelect::class => FormElementBrandSelectFactory::class
        ]
    ]
];

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...