Zend form and annotations validation - php

I'm trying using annotatnions to build and validate zend forms.
But currently I recive an error when I open showformAction:
"Fatal error: Uncaught exception 'Zend\Form\Exception\InvalidElementException' with message 'No element by the name of [username] found in form' ..."
So below is my code. What I doing wrong ?
Entity\User.php
namespace Application\Model;
use Zend\Form\Annotation;
/**
* #Annotation\Hydrator("Zend\Stdlib\Hydrator\ObjectProperty")
* #Annotation\Name("user")
*/
class User
{
/**
* #Annotation\Attributes({"type":"text" })
* #Annotation\Validator({"type":"Regex","options":{"regex":"/^[a-zA-Z][a-zA-Z0-9_-]{1,19}/"}})
* #Annotation\Options({"label":"Username:"})
*/
public $username;
}
Controller\ProductsController.php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Json\Json;
use Zend\View\Model\JsonModel;
use Zend\View\Model\ViewModel;
use Zend\Debug\Debug;
use Application\Entity\Products;
use Application\Entity\Category;
use Application\Form\ProductsForm;
use Doctrine\ORM\EntityManager;
use Application\Model\User;
use Zend\Form\Annotation\AnnotationBuilder;
class ProductsController extends AbstractActionController {
protected $albumTable;
protected $em;
protected $form;
public function savetodb($data) {
//code save to db ....
}
protected function getForm() {
$entity = new User();
$builder = new AnnotationBuilder();
$this->form = $builder->createForm($entity);
return $this->form;
}
public function showformAction() {
$viewmodel = new ViewModel();
$form = $this->getForm();
$request = $this->getRequest();
//disable layout if request by Ajax
$viewmodel->setTerminal($request->isXmlHttpRequest());
$is_xmlhttprequest = 1;
if (!$request->isXmlHttpRequest()) {
//if NOT using Ajax
$is_xmlhttprequest = 0;
if ($request->isPost()) {
$form->setData($request->getPost());
if ($form->isValid()) {
//save to db <span class="wp-smiley emoji emoji-wink" title=";)">;)</span>
$this->savetodb($form->getData());
}
}
}
$viewmodel->setVariables(array(
'form' => $form,
// is_xmlhttprequest is needed for check this form is in modal dialog or not
// in view
'is_xmlhttprequest' => $is_xmlhttprequest
));
return $viewmodel;
}
public function validatepostajaxAction() {
$form = $this->getForm();
$request = $this->getRequest();
$response = $this->getResponse();
$messages = array();
if ($request->isPost()) {
$form->setData($request->getPost());
if (!$form->isValid()) {
$errors = $form->getMessages();
foreach ($errors as $key => $row) {
if (!empty($row) && $key != 'submit') {
foreach ($row as $keyer => $rower) {
//save error(s) per-element that
//needed by Javascript
$messages[$key][] = $rower;
}
}
}
}
if (!empty($messages)) {
$response->setContent(\Zend\Json\Json::encode($messages));
} else {
//save to db <span class="wp-smiley emoji emoji-wink" title=";)">;)</span>
$this->savetodb($form->getData());
$response->setContent(\Zend\Json\Json::encode(array('success' => 1)));
}
}
return $response;
}
}

Your annotation should be
/**
* #Annotation\Type("Zend\Form\Element\Text")
* #Annotation\Validator({"type":"Regex","options":{"regex":"/^[a-zA-Z][a-zA-Z0-9_-]{1,19}/"}})
* #Annotation\Options({"label":"Username:"})
*/

Related

ZF2 - Set cookie in dispatch listener

I would like to set a cookie in a listener, if the query parameter "source" is set. I tried the following but the cookie does not exist.
How can I set the cookie correct?
class DispatchListener extends AbstractListenerAggregate {
public function attach(EventManagerInterface $eventManager) {
$this->listeners[] = $eventManager->getSharedManager()->attach(
'Zend\Stdlib\DispatchableInterface',
MvcEvent::EVENT_DISPATCH,
array($this, 'setCookie'),
-80
);
}
/**
* #var \Zend\Stdlib\RequestInterface
*/
protected $request;
/**
* #param \Zend\Stdlib\RequestInterface $request
*/
public function __construct(RequestInterface $request) {
$this->request = $request;
}
public function setCookie(EventInterface $event) {
if ($source = $this->request->getQuery('source')) {
$this->request->setCookies([
'source' => $source
]);
}
}
}
--------------------------------- UPDATE -----------------------------------
class Module implements ConfigProviderInterface, BootstrapListenerInterface {
public function onBootstrap(EventInterface $event) {
$target = $event->getTarget();
$serviceManager = $target->getServiceManager();
$eventManager = $target->getEventManager();
$eventManager->attach($serviceManager->get('Application\Listener\Dispatch'));
}
}
Seems like there are a few issues with your code, which we can rule out first.
You haven't provided code for how you are attaching your listener
Not sure why you are setting the cookie on the request, do you want to do this on the response?
Attaching the event listener:
public function onBootstrap(MvcEvent $e)
{
$eventManager = $e->getApplication()->getEventManager();
$app = $e->getApplication();
$em = $app->getEventManager();
// Attach event to attach listener after routing when query will be populated
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($eventManager) {
$request = $e->getRequest();
// attach our listener
$eventManager->attach(new DispatchListener($request));
});
}
Updated setCookie method:
public function setCookie(EventInterface $event) {
if ($source = $this->request->getQuery('source')) {
$this->request->setCookies([
'source' => $source
]);
$request = $this->request;
$cookieData = $request->getCookie('someCookie', 'default');
var_dump($cookieData);
}
}
The var_dump prints the following:
object(Zend\Http\Header\Cookie)[274]
protected 'encodeValue' => boolean true
private 'storage' (ArrayObject) =>
array (size=1)
'source' => string 'test' (length=4)
Do you want a cookie in your request object or do you want to create a cookie for the response. You probably want to create a cookie and set it on the response object.
Check for example this answer.
public function setCookie(EventInterface $event) {
if ($source = $this->request->getQuery('source')) {
$cookie = new \Zend\Http\Header\SetCookie('source', $source);
$headers = $this->getResponse()->getHeaders();
$headers->addHeader($cookie);
}
}

Dynamically disable form in form events symfony

I have many entities, each of which has its own form type. Some of the entities implements FooBarInterface comprising method FooBarInterface::isEnabled();
I want to create a Form Extension, to be check data_class at all forms, and disable form if entity implements FooBarInterface and entity::isEnabled() return false.
<?php
namespace AppBundle\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
class MyExtension extends AbstractTypeExtension
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$dataClass = $builder->getDataClass();
if ($dataClass) {
$reflection = new \ReflectionClass($dataClass);
if ($reflection->implementsInterface(FooBarInterface::class)) {
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $formEvent) {
$data = $formEvent->getData();
if ($data && !$data->isEnabled()) {
// todo this need disable all form with subforms
}
});
}
}
}
public function getExtendedType()
{
return 'form';
}
}
I have to make it through the $ builder-> addEventListener, because $ builder-> getData () do not always have the time to create a form. But after the creation of the form, I can not change her option disabled
How do I change the option disabled in the form?
I created Form Extension with method
public function finishView(FormView $view, FormInterface $form, array $options)
{
$dataClass = $form->getConfig()->getDataClass();
if ($dataClass) {
$reflection = new \ReflectionClass($dataClass);
if ($reflection->implementsInterface(FooBarInterface::class)) {
/** #var FooBarInterface$data */
$data = $form->getData();
if ($data && !$data ->isEnabled()) {
$this->recursiveDisableForm($view);
}
}
}
}
private function recursiveDisableForm(FormView $view)
{
$view->vars['disabled'] = true;
foreach ($view->children as $childView) {
$this->recursiveDisableForm($childView);
}
}
For disable changes if data is submitted I create Doctrine Event Subscriber with FormEvents::PRE_UPDATE event:
/**
* #param PreUpdateEventArgs $args
*/
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getEntity();
if (!$entity instanceof DataStateInterface) {
return;
}
if (!$entity->isEnabled()) {
$args->getEntityManager()->getUnitOfWork()->clearEntityChangeSet(spl_object_hash($entity));
}
}

Validation of embedded forms with Symfony2

I have a Parents form embedded into another form Student containing the data of the parents of a student. I need to validate the embedded form, because in my code just makes the validation of another form.
StudentType.php
//...
->add('responsible1', new ParentsType(),array('label' => 'Mother'))
->add('responsible2', new ParentsType(),array('label'=> 'Father'))
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BackendBundle\Entity\Student'
));
}
Entity Parents
//...
/**
* #ORM\OneToMany(targetEntity="Student", mappedBy="$responsible1")
* #ORM\OneToMany(targetEntity="Student", mappedBy="$responsible2")
*/
private $students;
Entity Student
//...
/**
*
* #ORM\ManyToOne(targetEntity="Parents", inversedBy="students", cascade={"persist"})
*/
private $responsible1;
/**
*
* #ORM\ManyToOne(targetEntity="Parents", inversedBy="students", cascade={"persist"})
*/
private $responsible2;
Using the following code in the controller I got the name and the error message of all invalid fields in the main form (Student), but I get get errors embedded forms (Parents), just get the name of the object (responsible1 or responsible2) and the message I get [object Object].
StudentController.php
protected function getErrorMessages(\Symfony\Component\Form\Form $form)
{
$errors = array();
foreach ($form->getErrors() as $key => $error) {
$errors[] = $error->getMessage();
}
foreach ($form->all() as $child) {
if (!$child->isValid()) {
$errors[$child->getName()] = $this->getErrorMessages($child);
}
}
return $errors;
}
/**
* Creates a new Student entity.
*
*/
public function createAction(Request $request)
{
// if request is XmlHttpRequest (AJAX) but not a POSt, throw an exception
if ($request->isXmlHttpRequest() && !$request->isMethod('POST')) {
throw new HttpException('XMLHttpRequests/AJAX calls must be POSTed');
}
$entity = new Student();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
if ($request->isXmlHttpRequest()) {
return new JsonResponse(array('message' => 'Success!'), 200);
}
return $this->redirect($this->generateUrl('student_show', array('id' => $entity->getId())));
}
if ($request->isMethod('POST')) {
return new JsonResponse(array(
'result' => 0,
'message' => 'Invalid form',
'data' => $this->getErrorMessages($form)),400);
}
return $this->render('BackendBundle:Student:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
I tried the above code with the function getErrorsAsString() for errors in a string and so if they appear all, so I guess I'll have to add something in the above code to validate objects when "responsible1" or "responsible2" validate all fields.
I need to get all those errors are invalid fields on both forms.I read something to add 'cascade_validation' => true , validation_group or #Assert\Valid() by the code, but I tried and I failed to get it. If someone can explain to me a little worth those, I thank you because I'm new to all this.
Following example flatterns form and subform errors into assoc array, let me know if this is what you are trying to achieve
<?php
namespace Example\Bundle\UtilityBundle\Form;
use Symfony\Component\Form\Form;
class FormErrors
{
public function getArray(Form $form, $style = 'KO')
{
$method = sprintf('get%sErrors', $style);
$messages = $this->$method($form->all());
return $messages;
}
private function getKOErrors(Form $children)
{
$errors = array();
/* #var $child \Symfony\Component\Form\Form */
foreach ($children as $child) {
$type = $child->getConfig()->getType()->getName();
if ($child->count() && ($type !== 'choice')) {
$childErrors = $this->getKOErrors($child->all());
if (sizeof($childErrors)) {
$errors = array_merge($errors, $childErrors);
}
} else {
if (!$child->isValid()) {
// I need only one error message per field
$errors[$child->getName()] = $child->getErrors()->current()->getMessage();
}
}
}
return $errors;
}
}

Symfony2 extended SonataUserController not changing

I want to change the templating of the registration form in my project by extending the new twig layout. However it does not change. It doesnt show any errors, but I am still getting the original view of the form. I did everything I found in documentation, but it still wont change, why?
1) I extended the userBundle.
2) I made created the ApplicationSonataUserBundle and did this:
class ApplicationSonataUserBundle extends Bundle
{
/**
* {#inheritdoc}
*/
public function getParent()
{
return 'SonataUserBundle';
}
}
I made my new controller and overwrited the old one(I only changed the rendered layout):
<?php
namespace Application\Sonata\UserBundle\Controller;
use Sonata\UserBundle\Controller\RegistrationFOSUser1Controller as BaseController;
class Registration1Controller extends BaseController
{
public function registerAction()
{
$user = $this->container->get('security.context')->getToken()->getUser();
if ($user instanceof UserInterface) {
$this->container->get('session')->getFlashBag()->set('sonata_user_error', 'sonata_user_already_authenticated');
$url = $this->container->get('router')->generate('sonata_user_profile_show');
return new RedirectResponse($url);
}
$form = $this->container->get('sonata.user.registration.form');
$formHandler = $this->container->get('sonata.user.registration.form.handler');
$confirmationEnabled = $this->container->getParameter('fos_user.registration.confirmation.enabled');
$process = $formHandler->process($confirmationEnabled);
if ($process) {
$user = $form->getData();
$authUser = false;
if ($confirmationEnabled) {
$this->container->get('session')->set('fos_user_send_confirmation_email/email', $user->getEmail());
$url = $this->container->get('router')->generate('fos_user_registration_check_email');
} else {
$authUser = true;
$route = $this->container->get('session')->get('sonata_basket_delivery_redirect');
if (null !== $route) {
$this->container->get('session')->remove('sonata_basket_delivery_redirect');
$url = $this->container->get('router')->generate($route);
} else {
$url = $this->container->get('session')->get('sonata_user_redirect_url');
}
}
$this->setFlash('fos_user_success', 'registration.flash.user_created');
$response = new RedirectResponse($url);
if ($authUser) {
$this->authenticateUser($user, $response);
}
return $response;
}
$this->container->get('session')->set('sonata_user_redirect_url', $this->container->get('request')->headers->get('referer'));
return $this->container->get('templating')->renderResponse('MpShopBundle:Frontend:registration.html.'.$this->getEngine(), array(
'form' => $form->createView(),
));
}
}
3) I added new Application\Sonata\UserBundle\ApplicationSonataUserBundle(), to the AppKernel.
Did I miss anything? What can be the problem?
UPDATE :
Now I am getting this error: Compile Error: Namespace declaration statement has to be the very first statement in the script. but my namespace is the first statement isnt it?

how to get acces to repository from a listener

I would like to make a request to a database from a subscriber
From a repository: it 's easy I use the following (for exemple entity user where I want to retrieve user with id=1 .
$repository = $this->getDoctrine()->getManager->getRepository('NameBundle:User');
$user = $repository->find(1);
But how can I do the it from a suscriber.
Here what I tried... (without sucess as I got the following from Symfony2: "FatalErrorException: Error: Call to undefined method Sdz\BlogBundle\Form\EventListener\Subscriber::getEntityManager()"
namespace Sdz\BlogBundle\Form\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Doctrine\ORM\EntityRepository;
class TraductionSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(FormEvent $event)
{
$form = $event->getForm();
$datas = $event->getData();
$userexist == 'no'
foreach ($datas as $data){
if ($data->getUser()->getId() == 1) { $userexist = 'yes'; $user= $data->getUser(); }
}
if ($userexist == 'no') {
$repository = $this->getEntityManager()->getRepository('SdzBlogBundle:User');
$user = $repository->find(1);
}
$form
->add('notes', 'collection', array(
'type' => new NoteType,
'label' => $user->getName(),
'required' => false,
));
}
}
Just pass it to constructor (if it's not a service):
class TraductionSubscriber implements EventSubscriberInterface
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
...
and use:
new TraductionSubscriber($em);
You can get Doctrine object via container.
use Symfony\Component\DependencyInjection\ContainerInterface;
[...]
$this->container->get('doctrine')->getManager();

Categories