How To Create Custom Form Elements That Extend Doctrine Entity Select Elements - zend-framework2

I am trying to create a custom element where I can pre-set an entity namespace say -- Application\Entity\User and easily add that element to any form. I have an issue with injecting the doctrine em in form elements -- I found this post about the form_elements key in configuration but it doesnt work -- so I figured maybe just setup the element and pass it the objectmanager from the form.
I want to do something like this.
$this->add(
array(
'name' => 'user_id',
'type' => 'Application\Form\Element\UserSelect',
'options' => array(
'label' => 'User',
'object_manager' => $this->getObjectManager(),
),
),
);
Or even better just this
$this->add(
array(
'name' => 'user_id',
'type' => 'Application\Form\Element\UserSelect',
'label' => 'User',
),
);
module.config.php
'service_manager' => array(
'aliases' => array(
'doctrine_service' => 'doctrine.entitymanager.orm_default',
),
'initializers' => array(
function ($instance, $sm) {
if ($instance instanceof DoctrineModule\Persistence\ObjectManagerAwareInterface) {
$instance->setObjectManager(
$sm->get('doctrine_service')
);
}
if ($instance instanceof Application\Form\AbstractForm) {
$instance->init();
}
},
),
),
AbstractForm.php
namespace Application\Form;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Zend\Form\Form as ZendForm;
abstract class AbstractForm extends ZendForm implements ObjectManagerAwareInterface
{
protected $objectManager;
public function setObjectManager(ObjectManager $objectManager)
{
$this->objectManager = $objectManager;
}
public function getObjectManager()
{
return $this->objectManager;
}
}
TestForm.php
namespace Users\Form;
use Application\Form\AbstractForm;
class Login extends AbstractForm
{
public function init()
{
$this->add(
array(
'name' => 'user_id',
'type' => 'DoctrineORMModule\Form\Element\DoctrineEntity',
'options' => array(
'label' => 'User',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Application\Entity\User',
'property' => 'name',
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array('is_deleted' => 0),
'orderBy' => array('name' => 'ASC'),
),
),
),
),
);
}
}

Add your initializer and invokables to the getFormElementConfig method of your Module.php:
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
...
public function getFormElementConfig()
{
return array(
'invokables' => array(
'MyForm' => 'Application\Form\MyForm',
),
'initializers' => array(
'ObjectManagerInitializer' => function ($element, $formElements) {
if ($element instanceof ObjectManagerAwareInterface) {
$services = $formElements->getServiceLocator();
$entityManager = $services->get('Doctrine\ORM\EntityManager');
$element->setObjectManager($entityManager);
}
},
),
);
}
Then use the FormElementManager to get te form:
$forms = $this->getServiceLocator()->get('FormElementManager');
$myForm = $forms->get('MyForm');
Finally add your element inside the init method - not the constructor because he will never be aware of the objectManager:
public function init()
{
$this->add(
array(
'name' => 'user_id',
'type' => 'DoctrineORMModule\Form\Element\DoctrineEntity',
'options' => array(
'label' => 'User',
'object_manager' => $this->getObjectManager(),
'target_class' => 'Application\Entity\User',
'property' => 'name',
'find_method' => array(
'name' => 'findBy',
'params' => array(
'criteria' => array('is_deleted' => 0),
'orderBy' => array('name' => 'ASC'),
),
),
),
),
);
}
See a discussion on this setup here.

Related

How can I use one form fieldset in nested collection?

I am creating page to insert data like a book. Where I'm using zend form fieldset and collection. Fieldset have only two fields heading field and content field. Heading and sub-heading have same content(fields).
Ex:
1: Heading Content
1.1: Sub Heading
Content
1.1.1: Sub Heading
Content
1.1.2: Sub Heading
Content
1.1.2.1: Sub Heading
Content
1.1.2.2: Sub Heading
Content
...
1.1.3: Sub Heading
Content
..
1.2: Sub Heading
Content
...
2: Heading Content
Note: here indexing is just to show the parent child relationship.
Code are below:
This is main form.
class BookPointlForm extends Form
{
public function __construct($name = null)
{
parent::__construct('Form');
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'points',
'attributes' => array(
'class'=> 'point_collection '
),
'options' => array(
'label' => 'Add Book Points',
'count' => 1,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'Book\Form\BookPointMainFieldset',
),
),
));
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Submit',
'id' => 'submitbutton',
'class' => 'btn btn-primary pull-right',
),
));
}
}
This fieldset is called in BookPointlForm's points collection
class BookPointMainFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct($name = null)
{
// we want to ignore the name passed
parent::__construct('Fieldset');
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'points',
'attributes' => array(
'class'=> 'point_collection '
),
'options' => array(
'label' => 'Add Book Points',
'count' => 1,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'Book\Form\BookPointFieldset',
),
),
));
$this->add(array(
'name' => 'add_nested_point',
'type' => 'Button',
'attributes' => array(
'value' => 'Add nested point',
'class'=> 'add_nested_point'
),
'options' => array(
'label' => 'Add Nested Point',
),
));
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'nested-points',
'attributes' => array(
'class'=> 'nested_point_collection '
),
'options' => array(
'label' => 'Add Book Points',
'count' => 1,
'should_create_template' => true,
'allow_add' => true,
'target_element' => array(
'type' => 'Book\Form\BookPointFieldset',
),
),
));
}
public function exchangeArray($data) {
}
public function getInputFilterSpecification()
{
return array(
'weight' => array(
'required' => true,
),
);
}
}
This is main fieldset it contents heading and heading's content
class BookHeadingFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct($name = null)
{
$kraHeadingText = '';
// we want to ignore the name passed
parent::__construct('BookHeadingFieldset');
// $this
// ->setHydrator(new ClassMethodsHydrator(false))
// ->setObject(new KraHeading())
;
$this->add(array(
'name' => 'id',
'type' => 'Hidden',
'attributes' => array(
'value' => ''
)
));
$this->add(array(
'name' => 'manualHeadingText',
'type' => 'Textarea',
'options' => array(
'label' => 'Heading',
'label_options' => [
'disable_html_escape' => true
],
),
'labelOptions' => array(
'disable_html_escape' => true,
),
'attributes' => array(
'class' => 'form-control',
'placeholder'=>"Enter heading",
'COLS'=>"150",
// 'required' => 'true',
),
));
}
public function exchangeArray($data) {
}
public function getInputFilterSpecification()
{
return array();
}
}
...

Validating Zend\Form\Element\Collection

I have a form which has 'rows' added dynamically using Zend\Form\Element\Collection. This works fine, but I am struggling to add the validation for these rows.
So far my code looks something like the following. I presume I need to pass something to InputFilter\InputFilter::add() but I can't figure out what:
<?php
class EditForm extends \Zend\Form\Form
{
public function __construct()
{
parent::__construct('edit');
$this->setUpFormElements();
$this->setupInputFilters();
}
protected function setUpFormElements()
{
$fieldset = new \Zend\Form\Fieldset;
$nameElement = new \Zend\Form\Element\Text('name');
$fieldset->add($nameElement);
$descriptionElement = new \Zend\Form\Element\Text('description');
$fieldset->add($description);
$this->add(
array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'rows',
'options' => array(
'label' => 'Edit Rows',
'should_create_template' => true,
'allow_add' => true,
'target_element' => $fieldset,
)
)
);
return $this;
}
public function setupInputFilters()
{
$filter = new \Zend\InputFilter\InputFilter();
$filter->add(
array(
'name' => 'rows',
'required' => true,
'validators' => array(
// Not sure what to do here!
)
)
);
return $this;
}
}
I think you need to add the input filter to the getInputFilterSpecification method of the fieldset you are adding dynamically
class Row extends Fieldset implements InputFilterProviderInterface
{
$this->add(array(
'name' => 'yourFieldset',
'options' => array(
'label' => 'One of your fieldset elements'
),
'attributes' => array(
'required' => 'required'
)
));
$this->add(array(
'name' => 'fields',
'options' => array(
'label' => 'Another fieldset element'
),
'attributes' => array(
'required' => 'required'
)
));
public function getInputFilterSpecification()
{
return array(
'yourFieldset' => array(
'required' => true,
),
'fields' => array(
'required' => true,
'validators' => array(
array(
'name' => 'Float'
)
)
)
);
}
then in your form you need to set the validation group
class EditForm extends Form
{
public function __construct()
{
$this->add(
array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'rows',
'options' => array(
'label' => 'Edit Rows',
'should_create_template' => true,
'allow_add' => true,
'target_element' => $fieldset,
)
)
);
$this->setValidationGroup(array(
'csrf',
'row' => array(
'yourFieldset',
'fields',
)
));
}
}

ZendFramework 2: No database adapter present

I have this class:
<?php
class RegisterFilter extends InputFilter
{
public function __construct()
{
$this->add(array(
'name' => 'email1',
'required' => true,
'validators' => array(
array(
'name' => 'EmailAddress',
'options' => array(
'domain' => true,
),
),
array(
'name' => 'Identical',
'options' => array(
'token' => 'email2',
),
),
array(
'name' => 'Db\NoRecordExists',
'options' => array(
'table' => 'user',
'field' => 'email',
'messages' => array(
'recordFound' => "Email already exist ... ! <br>",
),
),
),
),
));
}
}
?>
I get this error: No database adapter present. Any ideas why this happens?
It would be good if you'd read the documentation about Zend\Validator\Db\Record*. The given error message means exactly what it said. You don't provide a DB-Adapter inside the Validator.
From the DOCs:
$validator = new Zend\Validator\Db\RecordExists(
array(
'table' => 'users',
'field' => 'emailaddress',
'adapter' => $dbAdapter
)
);
If you want to find out how to get the DB-Adapter into your Form, i have written a Blog Article about said topic.

custom routing for content pages in zf2

Hello to all zend coders. I am newbie to zend, doing code for manage pages through DB.
I want to use URL for these pages like this:
www.domainname.com/about-us
www.domainname.com/contact-us
...
Table name is 'cms_page' & attributes are:
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`page_key` varchar(255) NOT NULL,
`content` longtext NOT NULL,
`added_on` datetime NOT NULL,
I have done R&D for this and here are code:
// in module.config.php
'navigation' => array(
'default' => array(
'account' => array(
'label' => 'Account',
'route' => 'node',
'params' => array(
'id' => '2',
),
'pages' => array(
'home' => array(
'label' => 'Dashboard',
'route' => 'node',
'params' => array(
'id' => '1',
'link' => '/test'
),
),
'login' => array(
'label' => 'Sign In',
'route' => 'node',
'params' => array(
'id' => '5',
'link' => '/about-us'
),
),
'logout' => array(
'label' => 'Sign Out',
'route' => 'node',
'params' => array(
'id' => '3',
),
),
),
),
),
),
'router' => array(
'routes' => array(
'home' => array(
'type' => 'Zend\Mvc\Router\Http\Literal',
'options' => array(
'route' => '/',
'defaults' => array(
'controller' => 'Application\Controller\Index',
'action' => 'index',
),
),
),
'node' => array(
'type' => 'Application\Router\Alias',
'options' => array(
'route' => '/node[/:id]',
'constraints' => array(
'id' => '[0-9]+'
),
'defaults' => array(
'__NAMESPACE__' => 'Application\Controller',
'controller' => 'Index',
'action' => 'index',
'id' => '0'
),
),
'may_terminate' => true,
),
),
),
// In Module.php
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$sm = $app->getServiceManager();
$sm->get('translator');
$sm->get('viewhelpermanager')->setFactory('controllerName', function($sm) use ($e) {
$viewHelper = new View\Helper\ControllerName($e->getRouteMatch());
return $viewHelper;
});
$eventManager = $e->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
$sm->setFactory('Navigation', 'Zend\Navigation\Service\DefaultNavigationFactory', true);
$alias = $sm->get('Application\Router\Alias');
$nav = $sm->get('Navigation');
$alias->setNavigation($nav);
}
public function getServiceConfig()
{
return array(
'factories' => array(
'Application\Router\Alias' => function($sm) {
$alias = new \Application\Router\Alias('/node[/:id]');
return $alias;
},
)
);
}
// Application\src\Application\Router
namespace Application\Router;
use Traversable;
use Zend\Mvc\Router\Exception;
use Zend\Stdlib\ArrayUtils;
use Zend\Stdlib\RequestInterface as Request;
use Zend\Mvc\Router\Http;
use Zend\Mvc\Router\Http\Segment as Segment;
class Alias extends Segment
{
private static $_navigation = null;
public function match(Request $request, $pathOffset = null)
{
$uri = $request->getUri();
$path = $uri->getPath();
$items = self::$_navigation->findAllBy('route', 'node');
$params = null;
if($items != null){
$t = sizeof($items);
for ($i=0; $i getParams();
if (isset($params['link']) && $params['link']==$path){
echo $uri->setPath('/'.$item->getRoute().'/'.$params['id']);
$request->setUri($uri);
break;
}
}
}
return parent::match($request, $pathOffset);
}
public function setNavigation($navigation){
self::$_navigation = $navigation;
}
protected function buildPath(array $parts, array $mergedParams, $isOptional, $hasChild, array $options)
{
if(isset($mergedParams['link'])){
return $mergedParams['link'];
}
return parent::buildPath($parts, $mergedParams, $isOptional, $hasChild, $options = array());
}
}
When I run this url www.domainname.com/about-us on browser getting 404 error.
Please help me where am I wrong and what code need to add or edit?
You can use the a Zend_Controller_Router_Route as an alternative. Here is a quick tutorial on how to do so:
http://www.codexperience.co.za/post/hiding-url-parameters-names-using-zend-routers

ZF2 - Multi navigation with navigation

Is it possible to have 2 differents navigation ?
For example :
//in module.config.php
'service_manager'=>array(
'factories'=>array(
'navigation1'=>'Zend\Navigation\Service\DefaultNavigationFactory',
'navigation2'=>'Zend\Navigation\Service\DefaultNavigationFactory',
),
),
'navigation'=>array(
'navigation1'=>array(
'home'=>array('type' => 'mvc','route' => 'home','active'=>false,'label' => 'Home','title' => 'Home',
'pages'=>array(
'contact'=>array('type' => 'mvc','route'=>'contact','active'=>false,'label'=>'Contact','title' =>'Contact'),
)
),
),
'navigation2'=>array(
'home'=>array('type'=>'mvc','route'=>'home','active'=>false,'label'=>'Home','title'=>'Home',
'contact'=>array('type'=>'mvc','route'=>'faq','active'=>false,'label'=>'Faq','title'=>'Faq'),
),
),
//Dans laout
<?php echo $this->navigation()->menu('navigation1')->setMinDepth(0);?>
<hr />
<?php echo $this->navigation()->menu('navigation2')->setMinDepth(0);?>
I would like 2 differents menu with differents pages but this method doesn't run.
Every one has an idea please ?
Thanks
Birzat
You need to provide a custom factory class for each navigation group. For example, see how ZfcAdmin does this:
Create a custom factory class
<?php
namespace ZfcAdmin\Navigation\Service;
use Zend\Navigation\Service\DefaultNavigationFactory;
class AdminNavigationFactory extends DefaultNavigationFactory
{
protected function getName()
{
return 'admin';
}
}
Source: https://github.com/ZF-Commons/ZfcAdmin/blob/master/src/ZfcAdmin/Navigation/Service/AdminNavigationFactory.php
Register AdminNavigationFactory
// in Module.php
public function getServiceConfig()
{
return array(
'factories' => array(
'admin_navigation' => 'ZfcAdmin\Navigation\Service\AdminNavigationFactory',
),
);
}
Source: https://github.com/ZF-Commons/ZfcAdmin/blob/master/Module.php#L90
Define Navigation trees in your module's configuration under the key you specified in the getName method of your factory. As an example, this is how ZfcUserAdmin adds itself to the ZfcAdmin menu:
'navigation' => array(
'admin' => array(
'zfcuseradmin' => array(
'label' => 'Users',
'route' => 'zfcadmin/zfcuseradmin/list',
'pages' => array(
'create' => array(
'label' => 'New User',
'route' => 'admin/create',
),
),
),
),
),
Source: https://github.com/Danielss89/ZfcUserAdmin/blob/master/config/module.config.php
/vendor/MyNamespace/library/MyNamespace/Navigation/Service/SecondaryNavigationFactory.php
namespace MyNamespace\Navigation\Service;
use Zend\Navigation\Service\DefaultNavigationFactory;
class SecondaryNavigationFactory extends DefaultNavigationFactory {
protected function getName() {
return 'secondary';
}
}
/config/autoload/global.php
return array(
'service_manager' => array(
'factories' => array(
'navigation' => 'Zend\Navigation\Service\DefaultNavigationFactory',
'secondary' => 'MyNamespace\Navigation\Service\SecondaryNavigationFactory',
),
),
'navigation' => array(
'default' => array(
array(
'label' => 'Item-1.1',
'route' => 'foo',
),
array(
'label' => 'Item-1.2',
'route' => 'bar',
),
),
'secondary' => array(
array(
'label' => 'Item-2',
'route' => 'baz',
),
),
),
);
/module/Application/view/layout/layout.phtml
<?php echo $this->navigation('navigation')->menu(); ?>
<?php echo $this->navigation('secondary')->menu(); ?>

Resources