Zend Framework 2 : Using DoctrineModule\Form\Element\ObjectSelect and custom repository
When we are using Doctrine2 in Zend Framework 2 project, we can use existing module named DoctrineModule that can be used to easist our job. Now I will explain about how to use
DoctrineModule\Form\Element\ObjectSelect
in our Form to load data into select element with custom query instead of default one.
For example, we have data like this :
Now, we need to build a form element that collect continent data, that’s means, we need to ‘group by continent’ for query-ing the table data like this :
To make it work, we need to create custom repository. Ok, let’s start.
1. Prepare the Entity
//module/Tutorial/src/Tutorial/Entity/Countries.php namespace Tutorial\Entity; use Doctrine\ORM\Mapping as ORM; /** * Countries * * @ORM\Table(name="countries") * @ORM\Entity(repositoryClass="Tutorial\Repository\CountriesRepository") */ class Countries { /** * @var integer * * @ORM\Column(name="id", type="bigint", nullable=false) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $id; /** * @var string * * @ORM\Column(name="country", type="string", length=30, nullable=false) */ private $country; /** * @var string * * @ORM\Column(name="continent", type="string", length=30, nullable=false) */ private $continent; /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set country * * @param string $country * @return Countries */ public function setCountry($country) { $this->country = $country; return $this; } /** * Get country * * @return string */ public function getCountry() { return $this->country; } /** * Set continent * * @param string $continent * @return Countries */ public function setContinent($continent) { $this->continent = $continent; return $this; } /** * Get continent * * @return string */ public function getContinent() { return $this->continent; } }
Above entity is pretty generic, but we add new “repositoryClass” attribute in the @ORM\Entity
annotation to linked with our custom repository.
2. Create a custom repository
//module/Tutorial/src/Tutorial/Repository/CountriesRepository.php namespace Tutorial\Repository; use Doctrine\ORM\EntityRepository; class CountriesRepository extends EntityRepository { public function getContinent() { $querybuilder = $this->createQueryBuilder('c'); return $querybuilder->select('c') ->groupBy('c.continent') ->orderBy('c.id', 'ASC') ->getQuery()->getResult(); } }
The getContinent
grab the countries data group by continent.
3. Create form
//module/Tutorial/src/Tutorial/Form/CountriesForm.php namespace Tutorial\Form; use Zend\Form\Form; use Zend\InputFilter\InputFilterProviderInterface; use Doctrine\ORM\EntityManager; class CountriesForm extends Form implements InputFilterProviderInterface { protected $entityManager; public function __construct(EntityManager $entityManager) { parent::__construct(); $this->entityManager = $entityManager; } public function init() { $this->add(array( 'name' => 'continent', 'type' => 'DoctrineModule\Form\Element\ObjectSelect', 'options' => array( 'object_manager' => $this->entityManager, 'target_class' => 'Tutorial\Entity\Countries', 'property' => 'continent', 'is_method' => true, 'find_method' => array( 'name' => 'getContinent', ), ), )); } public function getInputFilterSpecification() { return array(); // filter and validation here } }
We need to inject form with Doctrine\ORM\EntityManager
, and call the getContinent
method that we already define at our CountriesRepository. So the Form need to be created via factory :
//module/Tutorial/src/Tutorial/Factory/Form/CountriesFormFactory.php namespace Tutorial\Factory\Form; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; use Tutorial\Form\CountriesForm; class CountriesFormFactory implements FactoryInterface { public function createService(ServiceLocatorInterface $serviceLocator) { $services = $serviceLocator->getServiceLocator(); $entityManager = $services->get('Doctrine\ORM\EntityManager'); $form = new CountriesForm($entityManager); return $form; } }
4. Create controller
//module/Tutorial/src/Tutorial/Controller/CountriesController.php namespace Tutorial\Controller; use Zend\Mvc\Controller\AbstractActionController; use Zend\Form\FormInterface; use Zend\View\Model\ViewModel; class CountriesController extends AbstractActionController { protected $countriesForm; public function __construct(FormInterface $countriesForm) { $this->countriesForm = $countriesForm; } public function indexAction() { return new ViewModel(array( 'form' => $this->countriesForm, )); } }
We need to inject controller with the CountriesForm, so the Controller need to be created via factory :
//module/Tutorial/src/Tutorial/Factory/Controller/CountriesControllerFactory.php namespace Tutorial\Factory\Controller; use Zend\ServiceManager\FactoryInterface; use Zend\ServiceManager\ServiceLocatorInterface; use Tutorial\Controller\CountriesController; class CountriesControllerFactory implements FactoryInterface { public function createService(ServiceLocatorInterface $serviceLocator) { $services = $serviceLocator->getServiceLocator(); $countryForm = $services->get('FormElementManager')->get('Tutorial\Form\CountriesForm'); $controller = new CountriesController($countryForm); return $controller; } }
5. Register services
//module/Tutorial/config/module.config.php return array( 'doctrine' => array( 'driver' => array( 'Tutorial_Entities' => array( 'class' =>'Doctrine\ORM\Mapping\Driver\AnnotationDriver', 'cache' => 'array', 'paths' => array(__DIR__ . '/../src/Tutorial/Entity') ), 'orm_default' => array( 'drivers' => array( 'Tutorial\Entity' => 'Tutorial_Entities' ), ), ), ), 'controllers' => array( 'factories' => array( 'Tutorial\Controller\Countries' => 'Tutorial\Factory\Controller\CountriesControllerFactory', ), ), 'form_elements' => array( 'factories' => array( 'Tutorial\Form\CountriesForm' => 'Tutorial\Factory\Form\CountriesFormFactory', ), ), 'router' => array( 'routes' => array( 'countries' => array( 'type' => 'segment', 'options' => array( 'route' => '/countries[/:action]', 'constraints' => array( 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', ), 'defaults' => array( 'controller' => 'Tutorial\Controller\Countries', 'action' => 'index', ), ), ), ), ), 'view_manager' => array( 'template_path_stack' => array( 'tutorial' => __DIR__ . '/../view', ), ), );
6. Last but not least, build a view :
// module/Tutorial/view/tutorial/countries/index.phtml $form = $this->form; $form->prepare(); echo $this->form()->openTag($form); echo $this->formCollection($form); echo $this->form()->closeTag();
Ok, done ๐
33 comments