symfony 3: trying to transform the value of field using DataTransform - symfony-forms

I have added this model transformer following the docs:
->add('subtotal', MoneyType::class, array(
'attr' => array('readonly' => true),
))
->addModelTransformer(new CallbackTransformer(
// transform <br/> to \n so the textarea reads easier
function ($originalDescription) {
return $originalDescription;
},
function ($submittedDescription) {
var_dump($submittedDescription); //<<<<<<<<<<<<
// remove most HTML tags (but not br,p)
$cleaned = strip_tags($submittedDescription, '<br><br/><p>');
// transform any \n to real <br/>
return str_replace("\n", '<br/>', $cleaned);
}
))
The problem: the var_dump() you can see is outputting the whole entity as you can see below. I expected just the content of subtotal submitted field.
object(DefaultBundle\Entity\Bill)[682]
protected 'id' => null
private 'client' =>
object(Proxies\__CG__\DefaultBundle\Entity\Client)[788]
public '__initializer__' => null
public '__cloner__' => null
public '__isInitialized__' => boolean true
protected 'id' => int 1
private 'bills' (DefaultBundle\Entity\Client) =>
object(Doctrine\ORM\PersistentCollection)[436]
private 'snapshot' =>
array (size=0)
...
private 'owner' =>
&object(Proxies\__CG__\DefaultBundle\Entity\Client)[788]
private 'association' =>
array (size=15)
...
private 'em' =>
object(Doctrine\ORM\EntityManager)[151]
...
private 'backRefFieldName' => string 'client' (length=6)
private 'typeClass' =>
object(Doctrine\ORM\Mapping\ClassMetadata)[465]
...
private 'isDirty' => boolean false
protected 'collection' =>
object(Doctrine\Common\Collections\ArrayCollection)[801]
...
protected 'initialized' => boolean false
protected 'name' => string 'Jose Manuel Fernandez Fernandez' (length=31)
protected 'nif' => string '03113434P' (length=9)
protected 'address' => string 'Bulevar' (length=39)
protected 'phone' => string '633553423' (length=9)
protected 'email' => string 'me#gmail.com' (length=21)
protected 'numberPlate' => string 'fasdfdasf' (length=9)
protected 'createdAt' =>
object(DateTime)[453]
public 'date' => string '2016-02-28 00:00:00.000000' (length=26)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Madrid' (length=13)
protected 'serialNumber' => string '5' (length=1)
private 'servicesPerformed' =>
array (size=1)
0 =>
array (size=4)
'description' => string 'fasdf' (length=5)
'quantity' => string '4234' (length=4)
'unitPrice' => float 4234
'price' => float 17926756
protected 'subtotal' => null
protected 'taxRate' => float 0.21
protected 'tax' => float 3764618.76
protected 'total' => float 21691374.76
Here you have the entity and the form type:
class BillType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$em = $options['em'];
$builder
->add('serialNumber', TextType::class, array(
'label' => 'Nº serie',
'attr' => array('readonly' => true),
))
->add('createdAt', DateType::class, array(
'label' => 'Fecha',
'data' => new \DateTime("today"),
))
->add('client', null, array('label' => 'Cliente'))
->add('numberPlate', null, array(
'label' => 'Número matrícula',
//'data' => 'prueba'
))
->add('servicesPerformed', CollectionType::class, array(
'label' => false,
'entry_type' => ServicePerformedType::class,
'allow_add' => true,
'allow_delete' => true,
'attr' => array('data-prototype' => 'jander'),
))
->add('subtotal', MoneyType::class, array(
'attr' => array('readonly' => true),
//'grouping' => true
))
->addModelTransformer(new CallbackTransformer(
// transform <br/> to \n so the textarea reads easier
function ($originalDescription) {
return $originalDescription;
},
function ($submittedDescription) {
var_dump($submittedDescription);
die("jlfs");
// remove most HTML tags (but not br,p)
$cleaned = strip_tags($submittedDescription, '<br><br/><p>');
// transform any \n to real <br/>
return str_replace("\n", '<br/>', $cleaned);
}
))
->add('tax', MoneyType::class, array(
'label' => 'I.V.A.',
'attr' => array('readonly' => true),
'grouping' => true
))
->add('total', MoneyType::class, array(
'label' => 'Total',
'attr' => array('readonly' => true),
'grouping' => true
))
->add('Guardar', SubmitType::class)
->addEventListener(FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($em) {
$repository = $em->getRepository('DefaultBundle:Bill');
$result = count($repository->findAll()) + 1;
$form = $event->getForm();
$data = $event->getData();
if (!$data) {
$form->add('serialNumber', TextType::Class, array(
'data' => $result,
'label' => 'Nº serie',
'attr' => array('readonly' => true)
));
} else {
$form->add('serialNumber', TextType::Class, array(
'label' => 'Nº serie',
'attr' => array('readonly' => true)
));
}
}
)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'DefaultBundle\Entity\Bill',
'em' => ''
));
}
}
/**
* DefaultBundle\Entity\Bill
*
* #ORM\Table
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Bill
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Client", inversedBy="bills")
* #ORM\JoinColumn(name="client_id", referencedColumnName="id", nullable=false)
**/
private $client;
/**
* #ORM\Column(type="string")
*/
protected $numberPlate;
/**
* #ORM\Column(type="date")
*/
protected $createdAt;
/**
* #ORM\Column(type="string")
*/
protected $serialNumber;
/**
* Features of the product.
* Associative array, the key is the name/type of the feature, and the value the data.
* Example:<pre>array(
* 'size' => '13cm x 15cm x 6cm',
* 'bluetooth' => '4.1'
* )</pre>.
*
* #var array
* #ORM\Column(type="array")
*/
private $servicesPerformed = array();
/**
* #ORM\Column(type="float")
*/
protected $subtotal;
/**
* #ORM\Column(type="float")
*/
protected $taxRate = TaxRate::TAX_RATE;
/**
* #ORM\Column(type="float")
*/
protected $tax;
/**
* #ORM\Column(type="float")
*/
protected $total;
public function __toString()
{
return $this->numberPlate;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set numberPlate
*
* #param string $numberPlate
*
* #return Bill
*/
public function setNumberPlate($numberPlate)
{
$this->numberPlate = $numberPlate;
return $this;
}
/**
* Get numberPlate
*
* #return string
*/
public function getNumberPlate()
{
return $this->numberPlate;
}
/**
* Set servicesPerformed
*
* #param array $servicesPerformed
*
* #return Bill
*/
public function setServicesPerformed($servicesPerformed)
{
$this->servicesPerformed = $servicesPerformed;
return $this;
}
/**
* Get servicesPerformed
*
* #return array
*/
public function getServicesPerformed()
{
return $this->servicesPerformed;
}
/**
* Set subtotal
*
* #param string $subtotal
*
* #return Bill
*/
public function setSubtotal($subtotal)
{
$this->subtotal = $subtotal;
return $this;
}
/**
* Get subtotal
*
* #return string
*/
public function getSubtotal()
{
return $this->subtotal;
}
/**
* Set taxRate
*
* #param string $taxRate
*
* #return Bill
*/
public function setTaxRate($taxRate)
{
$this->taxRate = $taxRate;
return $this;
}
/**
* Get taxRate
*
* #return string
*/
public function getTaxRate()
{
return $this->taxRate;
}
/**
* Set tax
*
* #param string $tax
*
* #return Bill
*/
public function setTax($tax)
{
$this->tax = $tax;
return $this;
}
/**
* Get tax
*
* #return string
*/
public function getTax()
{
return $this->tax;
}
/**
* Set total
*
* #param string $total
*
* #return Bill
*/
public function setTotal($total)
{
$this->total = $total;
return $this;
}
/**
* Get total
*
* #return string
*/
public function getTotal()
{
return $this->total;
}
/**
* Set client
*
* #param \DefaultBundle\Entity\Client $client
*
* #return Bill
*/
public function setClient(\DefaultBundle\Entity\Client $client)
{
$this->client = $client;
return $this;
}
/**
* Get client
*
* #return \DefaultBundle\Entity\Client
*/
public function getClient()
{
return $this->client;
}
/**
* Set createdAt
*
* #param \DateTime $createdAt
*
* #return Bill
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* #return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set serialNumber
*
* #param string $serialNumber
*
* #return Bill
*/
public function setSerialNumber($serialNumber)
{
$this->serialNumber = $serialNumber;
return $this;
}
/**
* Get serialNumber
*
* #return string
*/
public function getSerialNumber()
{
return $this->serialNumber;
}
}
EDIT: after the answer of #ejuhjav this is my buildForm() function:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$em = $options['em'];
$builder
->add('serialNumber', TextType::class, array(
'label' => 'Nº serie',
'attr' => array('readonly' => true),
'data' => 6
))
->add('createdAt', DateType::class, array(
'label' => 'Fecha',
'data' => new \DateTime("today"),
))
->add('client', null, array('label' => 'Cliente'))
->add('numberPlate', null, array(
'label' => 'Número matrícula',
//'data' => 'prueba'
))
->add('servicesPerformed', CollectionType::class, array(
'label' => false,
'entry_type' => ServicePerformedType::class,
'allow_add' => true,
'allow_delete' => true,
'attr' => array('data-prototype' => 'jander'),
))
->add('subtotal', MoneyType::class, array(
'attr' => array('readonly' => true),
'data' => 6
))
->add('tax', MoneyType::class, array(
'label' => 'I.V.A.',
'attr' => array('readonly' => true),
'grouping' => true,
'data' => 6
))
->add('total', MoneyType::class, array(
'label' => 'Total',
'attr' => array('readonly' => true),
'grouping' => true,
'data' => 6
))
->add('Guardar', SubmitType::class)
;
$builder->get('subtotal')
->addModelTransformer(new CallbackTransformer(
// transform <br/> to \n so the textarea reads easier
function ($originalDescription) {
return $originalDescription;
},
function ($submittedDescription) {
//var_dump($submittedDescription);
die("IT IS _NOT_ ENTERING HERE!!! :)");
// remove most HTML tags (but not br,p)
$cleaned = strip_tags($submittedDescription, '<br><br/><p>');
// transform any \n to real <br/>
return str_replace("\n", '<br/>', $cleaned);
}
));
}

You are connecting your model transformer to the whole form. To add the transformer into single field use either (from the referenced docs):
$builder->add('description', TextareaType::class);
$builder->get('description')
->addModelTransformer(new CallbackTransformer(
...
or
$builder->add(
$builder->create('description', TextareaType::class)
->addModelTransformer(...)
);

Related

ZendFramework 2 - Problems with AbstractFactoryInterface; Param doesn't contain route/controller Namespace

I've got a module with one route and an abstract controller factory.
The route
namespace BaseApi;
use BaseApi\Factory\Controller\AbstractApiControllerFactory;
use Zend\Mvc\Router\Http\Segment;
return [
'router' => [
'routes' => [
'v2a-api' => [
'type' => Segment::class,
'options' => [
'route' => '/api/:controller',
'defaults' => [
'__NAMESPACE__' => 'BaseApi\Controller',
],
],
],
],
],
'controllers' => [
'abstract_factories' => [
AbstractApiControllerFactory::class,
],
],
];
The abstract controller factory
namespace BaseApi\Factory\Controller;
use BaseApi\Controller\ApiController;
use BaseApi\Exception\ApiAbstractControllerFactoryException;
use Zend\Filter\Inflector;
use Zend\Filter\StringToLower;
use Zend\Filter\Word\CamelCaseToDash;
use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
/**
* Class AbstractApiControllerFactory
*
* #package BaseApi\Factory\Controller
*/
class AbstractApiControllerFactory implements AbstractFactoryInterface
{
/**
* #param \Zend\ServiceManager\ServiceLocatorInterface $controllerManager
* #param $name
* #param $requestedName
*
* #return bool
*/
public function canCreateServiceWithName(ServiceLocatorInterface $controllerManager, $name, $requestedName)
{
dump('api', $name, $requestedName);
$serviceManager = $controllerManager->getServiceLocator();
$configKey = $this->getConfigKey($requestedName);
$config = $serviceManager->get('Config');
return (
strpos($requestedName, 'BaseApi\Controller') === 0
&& key_exists(
$configKey,
!empty($config['d3-api']) ? $config['d3-api'] : []
)
);
}
/**
* #param \Zend\ServiceManager\ServiceLocatorInterface $controllerManager
* #param $name
* #param $requestedName
*
* #return \BaseApi\Controller\ApiController
* #throws \BaseApi\Exception\ApiAbstractControllerFactoryException
*/
public function createServiceWithName(ServiceLocatorInterface $controllerManager, $name, $requestedName)
{
$serviceManager = $controllerManager->getServiceLocator();
$configKey = $this->getConfigKey($requestedName);
$config = $serviceManager->get('Config');
if (empty($config['d3-api'][$configKey]['service'])) {
throw ApiAbstractControllerFactoryException::missingServiceKey($configKey);
}
if (!$serviceManager->has($config['d3-api'][$configKey]['service'])) {
throw ApiAbstractControllerFactoryException::missingServiceException(
$config['d3-api'][$configKey]['service']
);
}
return new ApiController(
$serviceManager->get($config['d3-api'][$configKey]['service'])
);
}
/**
* Transforms Controller-Name + Namespace $requestedName in the related Config-Key
* :: BaseApi\Controller\GameLists => game-lists
*
* #param $requestedName
*
* #return string
*/
protected function getConfigKey($requestedName)
{
$controllerName = substr($requestedName, strrpos($requestedName, '\\') + 1);
$inflector = new Inflector(':classname');
$inflector->setFilterRule(
':classname',
[
CamelCaseToDash::class,
StringToLower::class,
]
);
return $inflector->filter(['classname' => $controllerName]);
}
}
Additional config (doesn't matter for this problem, only for completeness sake)
<?php
return [
'd3-api' => [
'customers' => [
'service' => 'V2aCustomerDb\Service\Customers',
],
],
];
Now my problem: I've got 2 ZF-MVC-Apps. In one, everything works fine; I see my objects when I start a request under /api/customers. In the other one, the parameter $requestedName of AbstractApiControllerFactory::canCreateServiceWithName doesn't contain the namespace. I only receive customers instead of BaseApi\Controller\customers
The project-setup is the same. Everything installed over composer has the same version. So it has to be a config-bug, but after several hours, I've got no clue, what it might be...
someone any suggestions?
After two days...
Module.php has to look like this (add BootstrapListenerInterface)
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* #link http://github.com/zendframework/ZendSkeletonApplication for the canonical source repository
* #copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
* #license http://framework.zend.com/license/new-bsd New BSD License
*/
namespace CustomerDatabase;
use Zend\EventManager\EventInterface;
use Zend\ModuleManager\Feature\BootstrapListenerInterface;
use Zend\Mvc\ModuleRouteListener;
/**
* Class Module
*
* #package CustomerDatabase
*/
class Module implements BootstrapListenerInterface
{
public function onBootstrap(EventInterface $event)
{
$eventManager = $event->getApplication()->getEventManager();
$moduleRouteListener = new ModuleRouteListener();
$moduleRouteListener->attach($eventManager);
}
[...]
}

Symfony form builder default select by EntityType

I try to create form with html select element using EntityType. I must get values by some condition and by this condition necessary default value not select from database. So i get all options values, without one, that must be a default value. So i try to find a way to put this value to select. What i tried...
Set value in form:
$growing = $em->getRepository('FarmBundle:Growing')->findGrowing($user_id);
$garden = $em->getRepository('FarmBundle:Garden')->find(7);
$tableForm = $this->createForm('FarmBundle\Form\GrowingType', $growing, ['user_id' => $user_id]);
$tableForm->get('garden')->setData($garden);
$form = $tableForm->createView()
Then i tried to set data in entity:
$growing = $em->getRepository('FarmBundle:Growing')->findGrowing($user_id);
$garden = $em->getRepository('FarmBundle:Garden')->find(7);
$growing->setGarden($garden);
$tableForm = $this->createForm('FarmBundle\Form\GrowingType', $growing, ['user_id' => $user_id]);
$form = $tableForm->createView()
Then i tried to set default select value in form_builder using 'data' attribute:
$growing = $em->getRepository('FarmBundle:Growing')->findGrowing($user_id);
$garden = $em->getRepository('FarmBundle:Garden')->find(7);
$tableForm = $this->createForm('FarmBundle\Form\GrowingType', $grow, [
'user_id' => $user_id,
'selected_choice' => $garden
]);
$form = $tableForm->createView();
Form_builder code:
class GrowingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', HiddenType::class)
->add('garden', EntityType::class , [
'class' => 'FarmBundle\Entity\Garden',
'query_builder' => function (GardenRepository $gr) use ($options) {
return $gr->queryFreeGardens($options['user_id']);
},
'attr' => [
'data-type' => 'text',
'class' => 'table-select',
'disabled' => true
],
'required' => false,
'data' => $options['selected_choice']
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'FarmBundle\Entity\Growing',
'selected_choice' => null,
'user_id' => null
));
}
}
And code of query for query builder:
class GardenRepository extends \Doctrine\ORM\EntityRepository
{
public function queryFreeGardens($user_id)
{
$qb = $this->createQueryBuilder('g')
->leftJoin('g.growing', 'grow')
->where('grow.plantDate is NULL')
->orWhere('grow.endDate is not NULL')
->andWhere('g.user = :user_id')
->orderBy('g.name')
->setParameter('user_id', $user_id);
return $qb;
}
}
And all of this 3 methods not works. Result is one, if entity not get for query in query builder, i cant set this entity. If i will set entity as default value, that was in query builder all will works fine.
How can i solve this problem?
try this
in controller:
$growing = $em->getRepository('FarmBundle:Growing')->findGrowing($user_id);
$garden = $em->getRepository('FarmBundle:Garden')->find(7);
$tableForm = $this->createForm('FarmBundle\Form\GrowingType', $grow, [
'user_id' => $user_id,
'additional_id' => 7
]);
$form = $tableForm->createView();
in form:
class GrowingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id', HiddenType::class)
->add('garden', EntityType::class , [
'class' => 'FarmBundle\Entity\Garden',
'query_builder' => function (GardenRepository $gr) use ($options) {
return $gr->queryFreeGardens($options['user_id'], $options['additional_id');
},
'attr' => [
'data-type' => 'text',
'class' => 'table-select',
'disabled' => true
],
'required' => false
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'FarmBundle\Entity\Growing',
'additional_id' => null,
'user_id' => null
));
}
}
in repository:
class GardenRepository extends \Doctrine\ORM\EntityRepository
{
public function queryFreeGardens($user_id, $additional_id)
{
$qb = $this->createQueryBuilder('g')
->leftJoin('g.growing', 'grow')
->where('grow.plantDate is NULL')
->andWhere('g.id = :additional_id')
->orWhere('grow.endDate is not NULL')
->andWhere('g.user = :user_id')
->andWhere('g.id = :additional_id')
->orderBy('g.name')
->setParameter('user_id', $user_id)->setParameter('additional_id', $additional_id);
return $qb;
}
}
maybe you will need to adjust your repository method to retrieve values in right way. There are or clause, you should add this additional id to both branches of your or clause. The main idea is to retrieve you selected object too.

Symfony form eventlistener string error

Entity\Profile.php
class Profile
{
...
/**
* #var string
*
* #ORM\Column(name="country", type="string", length=100, nullable=true)
*/
private $country;
/**
* #var string
*
* #ORM\Column(name="province", type="string", length=100, nullable=true)
*/
private $province;
...
}
MyProfileTypeForm.php:
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
->add('country', CountryType::class, array(
'label' => 'form.profile.country',
'preferred_choices' => array(
'US'
)
))
...
$builder->addEventListener(FormEvents::POST_SET_DATA, function(FormEvent $event) {
$form = $event->getForm();
$country = $form->get('country')->getData();
$form->add('province', EntityType::class, array(
'class' => 'UserBundle:LocationProvince',
'choice_label' => 'name',
'choice_value' => 'id',
'query_builder' => function (EntityRepository $er) use ($country) {
return $er
->createQueryBuilder('l')
->where('l.countryCode = :cc')
->setParameter(':cc', $country);
},
'label' => 'form.profile.province',
));
});
}
Error Code:
An exception occurred while executing 'UPDATE profile SET province = ? WHERE id = ?' with params [{}, 1]:
Catchable Fatal Error: Object of class Panel\UserBundle\Entity\LocationProvince could not be converted to string
Description:
The Entity of getting the country code. Provincial list drawn by the country code. But it does not record.
in your province choice drop-down list you've specified:
'choice_value' => 'id',
But I believe "id" is an integer.
You probably need to change this to:
'choice_value' => 'province',
Try that - I think it should work.

ZF2 + Doctrine2 Annotation Form Required & AllowEmpty

I use Annotation in a Doctrine Entity Class. Annotation for a field are :
/**
* #var integer
*
* #ORM\Column(name="duree", type="integer", nullable=true)
*
* #Form\Type("Zend\Form\Element\Number")
* #Form\Attributes({"required":false, "placeholder":"Durée", "min":"1", "max":"20"})
* #Form\Required(false)
* #Form\AllowEmpty()
* #Form\Options({"label":"Durée :"})
* #Form\Filter({"name": "Int"})
* #Form\Validator({"name":"IsInt"})
*/
private $duree;
So the DB column can be Empty (nullable), and in the form i wan't the same (ie user can leave input empty). I've both annotation Required(false) and allowEmpty, but the form never valid (always got isEmpty for this field).
If i set #Form\Type to "Text", it is working fine (form is valid event if input is empty). But with the class Number, it'is not the same.
I've the same pb with a Select element (correspondinf to a relationship). Annotations are :
/**
* #var \Application\Entity\CcCategorie
*
* #ORM\ManyToOne(targetEntity="Application\Entity\CcCategorie")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="categorie", referencedColumnName="id", nullable=true, onDelete="SET NULL")
* })
*
* #Form\Type("DoctrineModule\Form\Element\ObjectSelect")
* #Form\Attributes({"type":"select", "required":false})
* #Form\Options({"label":"Catégorie :"})
* #Form\Required(false)
* #Form\AllowEmpty()
* #Form\Options({
* "label":"Catégorie :",
* "empty_option": "---",
* "target_class": "Application\Entity\CcCategorie",
* "property": "label"
* })
*/
private $categorie;
But, with this the field has error (isEmpty) if the Select is set to empty option when validating Form.
The only workaround i've found is to set the annotation
* #Form\Type("\Application\Form\Entity\CcRepriseFieldset")
at the top of the entity class. The class CcRepriseFieldset extend Fieldset, and implement InputFilterProviderInterface. Then i specify the function in this class :
public function getInputFilterSpecification()
{
return array(
array(
"name" => "duree",
'required' => false,
'allow_empty' => true,
),
array(
"name" => "categorie",
'required' => false,
'allow_empty' => true,
),
);
}
With this it works... But it's not annotations.
I don't understand why annotation not work
thanks
Ok i found what is the probleme.
Annotation Builder made a array with all fields spec, like this :
array (size=7)
'name' => string 'reprise' (length=7)
'attributes' => array (size=0)
'elements' =>
array (size=8)
1 =>array (size=2)
...
'fieldsets' => array (size=0) empty
'type' => string '\Application\Form\Entity\CcRepriseFieldset'
(length=42)
'input_filter' =>
array (size=8)
'name' =>
array (size=4)
'name' => string 'name' (length=4)
'required' => boolean true
'filters' =>
array (size=3)
...
'validators' =>
array (size=2)
...
But this array is the Array for the FieldSet attached to the Entity.
So the Zend\Form\Factory never parse this because 'input_filter' are only parse for ZendForm Element not for fieldset (of course because Fieldset doesn't have SetInputFilter method)...
Ok i've a workaround but i'm not very statisfied of it .
First i've create a new FormFactory :
namespace Application\Form;
use Zend\Form\FieldsetInterface;
Class EntityFormFactory extends \Zend\Form\Factory {
public function configureFieldset(FieldsetInterface $fieldset, $spec)
{
$fieldset=parent::configureFieldset($fieldset, $spec);
$fieldset->input_filter_factory= $spec['input_filter'];
return $fieldset;
}
}
So this factory add the "input_filter" to a input_filter_factory variable of the fieldset.
Then in the fieldset class is :
class CcRepriseFieldset extends Fieldset implements \Zend\InputFilter\InputFilterProviderInterface
{
public function getInputFilterSpecification()
{
if (isset($this->input_filter_factory)){
return $this->input_filter_factory;
}
}
}
And finally, when i use annotationbuilder i change the formfactory :
$builder = new AnnotationBuilder($this->_em);
$builder->setFormFactory(new EntityFormFactory());
With this all is working fine... I'm not sur it's the best way to do it.

How to set db adapter to Validator NoRecordExists and use it in controller?

I recently started learning ZF2 and hope someone can help me with this.
I am working my way through Rob Allen's Zend Framework 2 Tutorial (many thanks to #rob-allen).
Also I use a solution of #AlloVince and #Maciej How to set db adapter to Validator RecordExists in Zend Framework 2 (many thanks to both authors for it) and I confused because didn't to use this solution in editAction.
I see Fatal error: Call to a member function get() on a non-object in 'adapter' => $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter').
1) In the Module.php added
public function getServiceConfig()
{
return array(
'invokables' => array(
'RegionModel' => 'FcLibraries\Model\Region', //<-- added it
),
'factories' => array(
'FcLibraries\Model\RegionTable' => function ($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new RegionTable($dbAdapter);
return $table;
},
),
);
}
2) In the Region.php added
/**
* #var
*/
protected $serviceLocator;
/**
* #param \Zend\ServiceManager\ServiceLocatorInterface $serviceLocator
* #return Library
*/
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
/**
* #return \Zend\ServiceManager\ServiceLocatorInterface
*/
public function getServiceLocator()
{
return $this->serviceLocator;
}
and
$inputFilter->add($factory->createInput(array(
'name' => 'name',
'required' => true,
'filters' => $this->_filters,
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 30,
),
),
array(
'name' => 'Db\NoRecordExists',
'options' => array(
'table' => $this->table,
'field' => 'name',
//'exclude' => array(
// 'field' => 'id',
// 'value' => $this->id
//),
'adapter' => $this->getServiceLocator()->get('Zend\Db\Adapter\Adapter'),
),
),
),
)));
3) In the RegionController.php in addAction using
$model = $this->getServiceLocator()->get('RegionModel');
instead of $model = new Region();.
This works fine for addAction, But I can not understand how I should use it in editAction.
My
public function editAction()
{
$id = (int)$this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('zfcadmin/region', array(
'action' => 'add'
));
}
$data = $this->getRegionTable()->get($id);
$form = new RegionForm();
$form->bind($data);
$form->get('submitBtn')->setAttribute('value', 'Save');
$request = $this->getRequest();
if ($request->isPost()) {
$form->setInputFilter($data->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getRegionTable()->save($form->getData());
return $this->redirect()->toRoute('zfcadmin/regions');
}
}
return array(
'id' => $id,
'form' => $form,
);
}
My RegionTable has the following code:
/**
* #param \Zend\Db\Adapter\Adapter $adapter
*/
public function __construct(Adapter $adapter)
{
$this->adapter = $adapter;
$this->resultSetPrototype = new ResultSet();
$this->resultSetPrototype->setArrayObjectPrototype(new Region());
$this->initialize();
}
public function get($id)
{
$id = (int)$id;
$rowSet = $this->select(array('id' => $id));
$row = $rowSet->current();
if (!$row) {
throw new \Exception("Could not find row $id");
}
return $row;
}
Many thanks to all who will answer my question.
Best regards, Ruslan.
Instead of using the form filter from the entity which you gathered from the table you should instantiate a new entity via the service manager to use the database adapter.
You have a few options:
Move the input filter to its own class and instantiate via the service manager so your database adapter is injected.
Change the prototype object in the table gateway factory to be instantiated via the service manager factory.
Instantiate a separate entity via the service manager and get the input filter from there.
I personally would go for option 1 as it separates the code better.
Some examples:
Option 1 (my choice):
This involves moving the filter to its own file and class, creating a factory for it whilst injecting the database adapter. We will then, in the controller, get the filter via the service manager and apply the filter to the form.
So first move your filter to a file in ModName\src\ModName\Form\RegionFilter.php, obviosly replacing ModName with your module name.
and change the code to like so:
<?php
namespace Region\Form;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
use Zend\Db\Adapter\Adapter;
class RegionFilter implements InputFilterAwareInterface {
/**
* #var inputFilter
*/
protected $inputFilter;
/**
* #var Database Adapter
*/
protected $dbAdapter;
/**
* #param \Zend\InputFilter\InputFilterInterface $inputFilter
* #throws \Exception
*/
public function setInputFilter(InputFilterInterface $inputFilter) {
throw new \Exception("Not used");
}
/**
* #param \Zend\Db\Adapter $dbAdapter
*/
public function __construct(Adapter $dbAdapter) {
$this->dbAdapter = $dbAdapter;
}
/**
*
* #return Zend\Db\Adapter
*/
public function getDbAdapter() {
return $this->dbAdapter;
}
/**
* #return \Zend\InputFilter\InputFilter
*
* Get the input filter (build it first)
*/
public function getInputFilter() {
if (!$this->inputFilter) {
$inputFilter = new InputFilter();
$factory = new InputFactory();
$inputFilter->add($factory->createInput(array(
'name' => 'name',
'required' => true,
'filters' => $this->_filters,
'validators' => array(
array(
'name' => 'StringLength',
'options' => array(
'encoding' => 'UTF-8',
'min' => 1,
'max' => 30,
),
),
array(
'name' => 'Db\NoRecordExists',
'options' => array(
'table' => $this->table,
'field' => 'name',
//'exclude' => array(
// 'field' => 'id',
// 'value' => $this->id
//),
'adapter' => $this->getDbAdapter(),
),
),
),
)));
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
?>
You would then create a factory like so in Module.php:
public function getServiceConfig()
{
return array(
'factories' => array(
'ModName\Form\RegionFilter' => function($sm) {
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
return new RegionFilter($dbAdapter);
},
),
);
}
And finally in your controller, just do the following:
if ($request->isPost()) {
$filter = $this->getServiceLocator()->get('ModName\Form\RegionFilter');
$form->setInputFilter($filter->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getRegionTable()->save($form->getData());
return $this->redirect()->toRoute('zfcadmin/regions');
}
}
Option 2:
This involves constructing your table with an instance of Region injected. Then you can set the prototype to this.
So in your table construct:
public function __construct(Adapter $adapter, Region $region)
{
$this->adapter = $adapter;
$this->resultSetPrototype = new ResultSet();
$this->resultSetPrototype->setArrayObjectPrototype($region);
$this->initialize();
}
And then your factory:
public function getServiceConfig()
{
return array(
'invokables' => array(
'RegionModel' => 'FcLibraries\Model\Region',
),
'factories' => array(
'FcLibraries\Model\RegionTable' => function ($sm) {
$region = $sm->get('RegionModel');
$dbAdapter = $sm->get('Zend\Db\Adapter\Adapter');
$table = new RegionTable($dbAdapter,$region);
return $table;
},
),
);
}
You should be able to leave the rest of the code as is. Eg the controller. Now I have not tested this method so I'm not 100% it will work, but I think it should. The other two methods I have used previously myself.
Option 3 (the simplest):
This involves getting a separate region model via the service manager and using that to apply the input filter to the form.
public function editAction()
{
$id = (int)$this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('zfcadmin/region', array(
'action' => 'add'
));
}
$data = $this->getRegionTable()->get($id);
$form = new RegionForm();
$form->bind($data);
$form->get('submitBtn')->setAttribute('value', 'Save');
$request = $this->getRequest();
if ($request->isPost()) {
$region = $this->getServiceLocator()->get('RegionModel');
$form->setInputFilter($region->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getRegionTable()->save($form->getData());
return $this->redirect()->toRoute('zfcadmin/regions');
}
}
return array(
'id' => $id,
'form' => $form,
);
}
I have not tested the code but you should get the gist. Any questions just ask.
For doing the validation for "Username already exists or not", Do the following simple way of Service Manager config settings like:
// config/autoload/global.php
return array(
'db' => array(
'driver' => 'Pdo',
'dsn' => 'mysql:dbname=zf2tutorial;host=localhost',
),
'service_manager' => array(
'factories' => array(
'Zend\Db\Adapter\Adapter' => function ($serviceManager) {
$adapterFactory = new Zend\Db\Adapter\AdapterServiceFactory();
$adapter = $adapterFactory->createService($serviceManager);
\Zend\Db\TableGateway\Feature\GlobalAdapterFeature::setStaticAdapter($adapter);
return $adapter;
}
),
)
);
and add the following array into getInputFilter():
array(
'table' => 'users',
'field' => 'username',
'adapter' => \Zend\Db\TableGateway\Feature\GlobalAdapterFeature::getStaticAdapter();
)

Resources