Symfony2 - Callback validator on collection - symfony

In symfony2 I'm tying to use a callback to validate my form, but this callback is never called. The class wherein the callback is, is called in the main form through a collection.
Here is my code...
Main class :
class InscriptionType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('inscriptionReponses','collection',array('label'=>false,
'type'=>new InscriptionReponseType(),
'error_bubbling'=>false,
'by_reference'=>false))
;
}
}
InscriptionReponse class :
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContextInterface;
/**
* InscriptionReponse
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Ptolemee\ColloqueBundle\Entity\InscriptionReponseRepository")
* #Assert\Callback(methods={"formValidation"})
*/
class InscriptionReponse
{
/* ... some code ... */
public function formValidation(ExecutionContextInterface $context)
{
die('not dying ?');
}
}
I don't understand what's wrong... any help would be highly appreciated. tahnks.
Nicolas.

The callback function of the collection is being called when you add #Assert\Valid to the collection in the containing entity.
Let's say Inscription has a collection of InscriptionResponses:
class Inscription
{
/**
* #Assert\Valid()
*/
private $inscriptionResponses;
}
class InscriptionResponse
{
/**
* #Assert\Callback
*/
public function formValidation(ExecutionContextInterface $context)
{
die('dying');
}
}
This works regardless of the value of the error_bubbling option.

base on what is written in the documentation:
http://symfony.com/doc/current/reference/constraints/Callback.html
instead of
/**
* #Assert\Callback(methods={"formValidation"})
*/
class InscriptionReponse
{
you should move the annotation above the function itself
class InscriptionReponse
{
/**
* #Assert\Callback
*/
public function formValidation(ExecutionContextInterface $context)
{
die('not dying ?');
}
The method you used was valid in version 2.3, you use 2.4 probably now

Related

Symfony: use DataTransformer in formType via Dependency injection

I want to use my DataTransformer to convert base64 strings to fileSystem images.
my code:
services:
lion_visionomie_media_bundle_base64_data_transformer:
class: Lion\Visionomie\MediaBundle\Transformer\Base64DataTransformer
arguments: ['#sonata.media.generator.default', '#sonata.media.manager.media', '#sonata.media.pool', '#doctrine.orm.entity_manager']
lion_visionomie_media_bundle_slide_type:
class: Lion\Visionomie\MediaBundle\Form\SlideType
arguments: ['#lion_visionomie_media_bundle_base64_data_transformer']
tags:
- {name: form.type}
class Base64DataTransformer implements DataTransformerInterface
{
/**
* #var GeneratorInterface
*/
private $pathGenerator;
/**
* #var MediaManager
*/
private $mediaManager;
/**
* #var Pool
*/
private $pool;
/**
* #var EntityManagerInterface
*/
private $entityManager;
public function __construct(GeneratorInterface $generator, MediaManager $mediaManager, Pool $pool, EntityManagerInterface $entityManager)
{
$this->pathGenerator = $generator;
$this->mediaManager = $mediaManager;
$this->pool = $pool;
$this->entityManager = $entityManager;
}
/**
* #param mixed $value
* #return mixed
*/
public function transform($value)
{
// [...]
return $media;
}
/**
* #param mixed $value
* #return mixed
*/
public function reverseTransform($value)
{
/** #var Media $media */
$media = $value;
return base64_encode(file_get_contents($media->getPreviousProviderReference()));
}
}
class SlideType extends AbstractType
{
/**
* #var Base64DataTransformer
*/
private $base64DataTransformer;
public function __construct(Base64DataTransformer $base64DataTransformer)
{
dump($base64DataTransformer);
die;
$this->base64DataTransformer = $base64DataTransformer;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('active')
->add('background')
->addModelTransformer($this->base64DataTransformer)
->add('thumbnail')
;
}
My current problem is, that the base64DataTransformer variable in SlideType is null.
Can you tell me why is this null?
Maybe I can use the ContainerAwareInterface in my SlideType. But I don't want to put my dependencies manually to my DataTransformer Class.
If you want to register your form in the DIC with Symfony < 2.8, you should add the alias: <result of getName()> parameter to the form.type tag of the type. In your case, if SlideType::getName() returns slide, set the tag to { name: form.type, alias: slide } and make sure that the form is instantiated by passing the name, not the type instance:
// good
$form = $this->createForm('slide', ...);
// bad
$form = $this->createForm(new SlideType(), ...);

Every parent controller must have `get{SINGULAR}Action($id)` method when i have multi level sub resource in FOS Rest Bundle

I have three controller named BlogController, PostController, CommentController that CommentController is sub resource of PostController and PostController sub resource of BlogController.
/**
* #Rest\RouteResource("blog", pluralize=false)
*/
class BlogController extends FOSRestController
{
public function getAction($blogUri)
{
...
}
}
/**
* #Rest\RouteResource("post", pluralize=false)
*/
class PostController extends FOSRestController
{
public function getAction($postId)
{
...
}
}
/**
* #Rest\RouteResource("comment", pluralize=false)
*/
class CommentController extends FOSRestController
{
public function getAction($commentId)
{
...
}
}
routing.yml
mgh_blog:
resource: MGH\BlogBundle\Controller\BlogController
type: rest
mgh_blog_post:
resource: MGH\BlogBundle\Controller\PostController
type: rest
parent: mgh_blog
mgh_blog_post_comment:
resource: MGH\PostBundle\Controller\CommentController
type: rest
parent: mgh_blog_post
I define getAction methods, but i get following error:
[InvalidArgumentException]
Every parent controller must have `get{SINGULAR}Action($id)` method
where {SINGULAR} is a singular form of associated object
Edit:
I also try to change the method's name to getCommentAction($commentId), getPostAction($postId) and getBlogAction, but it no work.
When I use #RouteResource annotations, method name must be getAction($id), otherwise it doesn't work.
When I change parent of mgh_blog_post_comment router to mgh_blog, it's working!
That error description is awful and a big time waster because it doesn't tell you what the real problem is. Try the following:
/**
* #Rest\RouteResource("blog", pluralize=false)
*/
class BlogController extends FOSRestController
{
public function getAction($blogUri)
{
...
}
}
/**
* #Rest\RouteResource("post", pluralize=false)
*/
class PostController extends FOSRestController
{
public function getAction($blogUri, $postId)
{
...
}
}
/**
* #Rest\RouteResource("comment", pluralize=false)
*/
class CommentController extends FOSRestController
{
public function getAction($blogUri, $postId, $commentId)
{
...
}
}
You didn't have the correct number of arguments in the descendant controller actions. It took me two days of step debugging to figure this out.
The parent route, Blog, looks like:
/blog/{blogUri}
It will match
public function getAction($blogUri)
The child route, Post, looks like:
/blog/{blogUri}/post/{postId}
It will not match the code below because it needs two parameters. The same is true for the grandchild--which is looking for three parameters:
public function getAction($postId)
The grandchild route, Comment, looks like:
/blog/{blogUri}/post/{postId}/comment/{commentId}
The code keeps track of the ancestors of each controller.
The post has 1 ancestor. When building the routes for the post controller, the code looks at the number of parameters on the 'get action'. It take the number of parameters and subtracts the number of ancestors. If the difference is not equal to one, it throws the error.
Conclusion, for each descendant, it needs to include the ID parameters of it ancestors AND its own ID. There should always be one more parameter than there are ancestors.
Have you tried?:
class CommentController extends FOSRestController
{
public function getCommentAction($commentId)
{
...
}
}
Try:
public function cgetAction(Request $request)
{
...
}
This is my controller example:
<?php
namespace Cf\SClinicBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\Controller\Annotations\RouteResource;
use Cf\SClinicBundle\Entity\CfAcquireImage;
use Doctrine\DBAL\DBALException as DBALException;
use Doctrine\ORM\NoResultException as NoResultException;
/**
* CfAcquireImage controller.
*
* #RouteResource("acquire-image")
*/
class ApiCfAcquireImageController extends FOSRestController
{
/**
* #var array
*/
public $status;
/**
* #var
*/
public $parameter;
/**
* #var
*/
private $role_name;
/**
* Constructor
*/
public function __construct()
{
}
/**
* Lists all Cf Acquire Image entities.
*
* #param Request $request
*
* #return mixed
*/
public function cgetAction(Request $request)
{
}
/**
* Finds a Cf Acquire Image entity by id.
*
* #param Request $request
* #param $id $id
*
* #return array
*/
public function getAction(Request $request, $id)
{
}
/**
* Create a new Cf Acquire Image entity.
*
* #param Request $request
*
* #return mixed
*/
public function postAction(Request $request)
{
}
/**
* #param Request $request
* #param $id
*
* #return array
*/
public function putAction(Request $request, $id)
{
}
/**
* Deletes a Cf Acquire Image entity.
*
* #param Request $request
* #param $id
*
* #return mixed
*/
public function deleteAction(Request $request, $id)
{
}
}

Adding a file upload to a Symfony2 form throws an "Entity was not found" and a session is active error

I have a Symfony2 form that I want to add a file upload dialog to.
According to the Symfony docs (http://symfony.com/doc/2.0/cookbook/doctrine/file_uploads.html), I have created a Document class:
<?php
namespace Acme\AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Document
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
public $path;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
public function getWebPath()
{
return null === $this->path ? null : $this->getUploadDir().'/'.$this->path;
}
protected function getUploadRootDir()
{
// the absolute directory path where uploaded documents should be saved
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw when displaying uploaded doc/image in the view.
return 'uploads/documents';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
$this->path = sha1(uniqid(mt_rand(), true)).'.'.$this->file->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
$this->file->move($this->getUploadRootDir(), $this->path);
unset($this->file);
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);
}
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set path
*
* #param string $path
*/
public function setPath($path)
{
$this->path = $path;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
}
And a DocumentType form class:
<?php
namespace Acme\AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class DocumentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file')
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\AppBundle\Entity\Document',
));
}
public function getName()
{
return 'document_form';
}
}
However, when I add this to my existing entity and form class:
<?php
namespace Acme\AppBundle\Entity\Profile;
use Doctrine\ORM\Mapping as ORM;
use Acme\AppBundle\Entity\jDocument;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="UserProfile")
*/
class UserProfile extends GenericProfile
{
//... Other entity params
/**
* #ORM\OneToOne(cascade={"persist", "remove"}, targetEntity="Acme\AppBundle\Entity\Document")
* #ORM\JoinColumn(name="picture_id", referencedColumnName="id", onDelete="set null")
*/
protected $picture;
/**
* Set picture
*
* #param Acme\AppBundle\Entity\Document $picture
*/
//\Acme\AppBundle\Entity\Document
public function setPicture($picture)
{
$this->picture = $picture;
}
/**
* Get picture
*
* #return Acme\AppBundle\Entity\Document
*/
public function getPicture()
{
return $this->picture;
}
//... Other entity getters and setters
}
Whenever I submit the form, I get the following error:
ErrorException: Warning: ini_set(): A session is active. You cannot change the session module's ini settings at this time in /var/www/wolseley-integrated-services/builds/dev/app/cache/prod/classes.php line 421
But the page title is "Entity was not found. (500 Internal Server Error)".
Can anybody spot which entity it can't find? Or if that's even the issue?
I've done some googling and checked that session.auto_start is set to 0 in php.ini, I've cleared all my sessions and caches... I'm stumped!
It turns out I was getting this strange session error message because of an error page definition in my nginx config.
The entity not found issue was resolved by correcting some errors in my entities. The Symfony developer bar provided me with enough information to track the issue down.

Cross reference in Symfony2 form not work as expected

This is my first question here, so please excuse any mistakes - I'll try to avoid them the next time. ;-)
I've written a custom RegistrationFormType for the FOSUserBundle. This form handles - in addition to the default fields of the bundle - a PlayerType. This PlayerType itself again contains a PlayerSkillsType. Here the classes:
class RegistrationFormType extends BaseType
{
public function buildForm(FormBuilder $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->add('player', new PlayerType());
}
public function getName()
{
return 'signup_form';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Acme\AcmeBundle\Entity\User',
);
}
}
class PlayerType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('firstname');
$builder->add('lastname');
$builder->add('age');
$builder->add('playerSkills', new PlayerSkillsType());
}
public function getName()
{
return 'player_form';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Acme\AcmeBundle\Entity\Player',
);
}
}
class PlayerSkillsType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('tackling');
$builder->add('passing');
$builder->add('shooting');
}
public function getName()
{
return 'playerSkills_form';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Acme\AcmeBundle\Entity\PlayerSkills',
);
}
}
/**
* #ORM\Entity
*/
class Player
{
/**
* #ORM\OneToOne(targetEntity="PlayerSkills", cascade={"persist"})
*
* #var PlayerSkills
*/
private $playerSkills;
}
/**
* #ORM\Entity
*/
class PlayerSkills
{
/**
* #ORM\OneToOne(targetEntity="Player", cascade={"persist"})
*
* #var Player
*/
private $player;
}
(I've left out getters and setters and unimportant properties and methods.)
This is working fine so far, the form is shown and persisted. Now, my problem is, that after persisting the data, the PlayerSkills entity in the data is missing the reference back to the Player entity.
I think it's something that I need to tell the PlayerSkillsType that it shall also add the reference in the form builder..? Or maybe this is issue in the Doctrine annotations?
Any hint is very appreciated! :-)
The problem could come from the initialization of your data and/or doctrine mapping.
The form will create the data_class if none is passed using $form->setData.
When you submit the form and bind data, It will call $player>setPlayerSkills($playerSkill),
but it won't call $playerSkill->setPlayer($player);
Depending of the owning side of your oneToOne association, you should call one of the two methods so that Doctrine will be aware of this association ( http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html#owning-side-and-inverse-side ).
Try to modify your annotation mapping in PlayerSkills to introduce the inversedBy information also ( http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html#one-to-one-bidirectional ).
It should be something like this:
/**
* #ORM\OneToOne(targetEntity="Player", mappedBy="playerSkills", cascade={"persist"})
*
* #var Player
*/
private $player;
Same thing for the Player class:
/**
* #ORM\OneToOne(targetEntity="PlayerSkills", inversedBy="player" cascade={"persist"})
*
* #var PlayerSkills
*/
private $playerSkills;
Last, you can code your methods to automatically synchronize inverse side, as explained here: http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html#picking-owning-and-inverse-side .

FOSUserBundle Form Registration Overriding

I've got a problem when I want to override the FOSUserBundle registration Form.
The deal is, in the User entity, some of the users can have a "Sponsor" (a sponsor is a ManyToOne to the same entity), to be more explicit, this is the User Entity :
<?php
namespace Diz\UserBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* create FK "sponsor_id" referenced to the id field on the same table
* #ORM\ManyToOne(targetEntity="User")
* #ORM\JoinColumn(name="sponsor_id", referencedColumnName="id", onDelete="SET NULL")
*/
protected $sponsor;
public function __construct()
{
// import FOSUserBundle properities ->
parent::__construct();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set sponsor
*
* #param Dizsurf\UserBundle\Entity\User $sponsor
*/
public function setSponsor(\Dizsurf\UserBundle\Entity\User $sponsor)
{
$this->sponsor = $sponsor;
}
/**
* Get sponsor
*
* #return Dizsurf\UserBundle\Entity\User
*/
public function getSponsor()
{
return $this->sponsor;
}
}
You see ?
Then, to override the RegistrationFormType, I've created one with the official help :
<?php
namespace Diz\UserBundle\Form\Type;
use Symfony\Component\Form\FormBuilder;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
class RegistrationFormType extends BaseType
{
public function buildForm(FormBuilder $builder, array $options)
{
parent::buildForm($builder, $options);
// add your custom field
$builder->add('sponsor', 'fos_user_username');
}
public function getName()
{
return 'diz_user_registration';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Diz\UserBundle\Entity\User', // Ni de modifier la classe ici.
);
}
}
And that's all ! Look like to be pretty simple ! But...
To simply convert the username into a User Entity, FOS advice to use "fos_user_username" in the builder.
Ok for me, but when I test this form :
With a sponsor who does exist, I've got this error "Please enter a password". (of course I've entered the password twice..).
But when I submit a form with an user whose does not exist, the registration form was submitted with success !
Have I done something wrong ?
Thank you for your help ! ;-)
Dizda.
Fixed.
I've just upgraded symfony from 2.0.10 to 2.1 and the problem is not present anymore !

Resources