I have problem with a contact form in symfony2 here is the code what i've done and what error do i get
<?php
// src/Aleksandar/IntelMarketingBundle/Resources/views/ContactType.php
namespace Aleksandar\IntelMarketingBundle\Resources\views;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Collection;
class ContactType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'text', array(
'attr' => array(
'placeholder' => 'What\'s your name?',
'pattern' => '.{2,}' //minlength
)
))
->add('email', 'email', array(
'attr' => array(
'placeholder' => 'So I can get back to you.'
)
))
->add('subject', 'text', array(
'attr' => array(
'placeholder' => 'The subject of your message.',
'pattern' => '.{3,}' //minlength
)
))
->add('message', 'textarea', array(
'attr' => array(
'cols' => 20,
'rows' => 2,
'placeholder' => 'And your message to me...'
)
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$collectionConstraint = new Collection(array(
'name' => array(
new NotBlank(array('message' => 'Name should not be blank.')),
new Length(array('min' => 2))
),
'email' => array(
new NotBlank(array('message' => 'Email should not be blank.')),
new Email(array('message' => 'Invalid email address.'))
),
'subject' => array(
new NotBlank(array('message' => 'Subject should not be blank.')),
new Length(array('min' => 3))
),
'message' => array(
new NotBlank(array('message' => 'Message should not be blank.')),
new Length(array('min' => 5))
)
));
$resolver->setDefaults(array(
'constraints' => $collectionConstraint
));
}
public function getName()
{
return 'contact';
}
}
?>
This is the code for the contact form which will be rendered in the view
no here is the code from my controller
<?php
namespace Aleksandar\IntelMarketingBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class DefaultController extends Controller
{
/**
* #Route("/contact", _name="contact")
* #Template()
*/
public function contactAction()
{
$form = $this->createForm(new ContactType());
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
$message = \Swift_Message::newInstance()
->setSubject($form->get('subject')->getData())
->setFrom($form->get('email')->getData())
->setTo('info#intelmarketing.es')
->setBody(
$this->renderView(
'AleksandarIntelMarketingBundle::contact.html.php',
array(
'ip' => $request->getClientIp(),
'name' => $form->get('name')->getData(),
'message' => $form->get('message')->getData()
)
)
);
$this->get('mailer')->send($message);
$request->getSession()->getFlashBag()->add('success', 'Your email has been sent! Thanks!');
return $this->redirect($this->generateUrl('contact'));
}
}
return array(
'form' => $form->createView()
);
}
}
and here is the rooting
aleksandar_intel_marketing_contactpage:
pattern: /contact
defaults: { _controller: AleksandarIntelMarketingBundle:Default:contact }
now when i try to open the page its says the fallowing:
"[Semantical Error] The annotation "#Route" in method
Aleksandar\IntelMarketingBundle\Controller\DefaultController::contactAction()
was never imported. Did you maybe forget to add a "use" statement for
this annotation? 500 Internal Server Error - AnnotationException "
If any one knows what might be the problem please let me know
As the error message states, you are missing a use statement on top of your controller file.
Simply add:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
on top of your class DefaultController.
You can then replace your routing with:
aleksandar_intel_marketing:
resource: "#AleksandarIntelMarketingBundle/Controller/DefaultController.php"
type: annotation
This way, you are using the #Route annotation instead of the default yml way to declare your routes.
http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html
Related
How do I add validation constraints "Field is required" in symfony2 PostType class? Any suggestions? I am new in SF2 and I'm just editing what has been done by previous developer.
use Symfony\Component\Validator\Constraints\NotBlank;
class BlogPostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$blogPostCategories = BlogPostCategoryQuery::create()
->filterByBlogPost($builder->getData())
->find();
$categoryIds = array();
foreach ($blogPostCategories as $blogPostCategory) {
$categoryIds[] = $blogPostCategory->getCategory()->getId();
}
$queryOptions = array(
'option_status' => Categorypeer::STATUS_ACTIVE,
'option_category_ids' => $categoryIds
);
$categories = CategoryQuery::create()
->filterActiveCategoriesByOptions($queryOptions)
->find();
$builder->add('category_ids', 'model', array(
'label' => 'Category',
'mapped' => false,
'class' => 'Deal\MainBundle\Model\Category',
'query' => CategoryQuery::create()
->filterActiveCategoriesByOptions()
->orderByName(),
'property' => 'name',
'empty_value' => 'Select categories',
'empty_data' => null,
'required' => true,
'multiple' => true,
'data' => $categories,
'constraints' => array(
new NotBlank(array(
'message' => 'Your message can not be blank! Ouch!'
)),
)
));
Thank you
You should set required => true for this type from the parent form when calling (that's the form type that uses your BlogPostType):
$formBuilder->add('whatever', BlogPostType::class, ['required' => true]);
Also, you could set required = true by default for BlogPostType:
class BlogPostType extends AbstractType
{
// ...
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'required' => true
]);
}
}
You can do it with constraints key. Like this:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('message', TextareaType::class, array(
'required' => true,
'constraints' => array(
new NotBlank(array(
'message' => 'Your message can not be blank! Ouch!'
)),
)
))
;
}
do not forget to add uses:
use Symfony\Component\Validator\Constraints\NotBlank;
I have a Symfony 2 entity. When I create a new record, I must fill all the values using a form, but after saving it, one of the values, $amount shouldn't be updatable when I update the others members.
How can I accomplish this? It's possible to mark a form member as a read-only, in runtime?
By using the validation_groups and name options when creating your form, you can change the form.
The name attribute sets the form creation, and the validation_groups takes care of the validation.
For example, in the create/new method of your controller;
public function createAction(Request $request)
{
// Instantiate new Foo object
$client = new Foo();
// create the form (setting validation group)
$form = $this->formFactory->create('foo', $foo, array(
'name' => 'create',
'validation_groups' => array('create')
)
);
// form has been submitted...
if ('POST' === $request->getMethod()) {
// submits the form
$form->handleRequest($request);
// do validation
if ($form->isValid()) {
// do whatever
}
}
// either GET or validation failed, so show the form
return $this->template->renderResponse('FooBundle:foo:add.html.twig', array(
'form' => $form->createView(),
'foo' => $foo
));
}
And in the edit/update function of your controller;
public function updateAction($id, Request $request)
{
// Instantiate Client object
$client = new Foo($id);
// create the form (setting validation group)
$form = $this->formFactory->create('foo', $foo, array(
'name' => 'update',
'validation_groups' => array('update')
));
// form has been submitted...
if ('POST' === $request->getMethod()) {
// submits the form
$form->handleRequest($request);
// do validation
if ($form->isValid()) {
// do whatever
}
}
// either GET or validation failed, so show the form
return $this->template->renderResponse('FooBundle:foo/edit:index.html.twig', array(
'form' => $form->createView(),
'foo' => $foo
));
}
And your Form Type will look something like;
class FooType extends BaseAbstractType
{
protected $options = array(
'data_class' => 'FooBundle\Model\Foo',
'name' => 'foo',
);
private $roleManager;
public function __construct($mergeOptions = null)
{
parent::__construct($mergeOptions);
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->$options['name']($builder, $options);
}
private function create(FormBuilderInterface $builder, array $options)
{
// ID
$builder->add('Id', 'text', array(
'required' => true,
'label' => 'ID',
'attr' => array(
'placeholder' => 'Format: 2 alphanumeric (e.g. A1)'
)
));
// Name - only show on create
$builder->add('Name', 'text', array(
'required' => true,
'label' => 'Name',
'attr' => array(
'placeholder' => 'Your name'
)
));
// add the submit form button
$builder->add('save', 'submit', array(
'label' => 'Save'
));
}
private function update(FormBuilderInterface $builder, array $options)
{
// ID
$builder->add('Id', 'text', array(
'required' => true,
'label' => 'ID',
'attr' => array(
'placeholder' => 'Format: 2 alphanumeric (e.g. A1)',
)
));
// Name - just for show
$builder->add('Name', 'text', array(
'required' => true,
'label' => 'Name',
'attr' => array(
'readonly' => 'true' // stops it being editable
)
));
// add the submit form button
$builder->add('save', 'submit', array(
'label' => 'Save'
));
}
}
P.S. All my classes are declared as services, so how you call create forms/views/etc may be different.
I asked a question here How to use Repository custom functions in a FormType but nobody anwsered, so i did a little digging and advanced a little but i still get this error:
Notice: Object of class Proxies\__CG__\Kpr\CentarZdravljaBundle\Entity\Category
could not be converted to int in /home/kprhr/public_html/CZ_Symfony/vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/ChoiceList/ChoiceList.php line 457
Now this is how my CategoryType looks like:
<?php
namespace Kpr\CentarZdravljaBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Bridge\Doctrine\RegistryInterface;
class CategoryType extends AbstractType
{
private $doctrine;
public function __construct(RegistryInterface $doctrine)
{
$this->doctrine = $doctrine;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Kpr\CentarZdravljaBundle\Entity\Category',
'catID' => null,
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$someId = $builder->getData()->getId();
$param = ($someId) ? $someId : 0;
$catID = $options['catID'];
$builder->add('name', 'text', array('attr' => array('class' => 'span6')));
$builder->add('file', 'file', array('image_path' => 'webPath', 'required' => false));
$builder->add('parent', 'choice', array(
'choices' => $this->getAllChildren($catID),
'required' => false,
'attr' => array('data-placeholder' => '--Izaberite Opciju--'),
));
$builder->add('tags', 'tag_selector', array(
'required' => false,
));
$builder->add('status', 'choice', array(
'choices' => array('1' => 'Aktivna', '0' => 'Neaktivna'),
'required' => true,
));
$builder->add('queue', 'text', array('attr' => array('class' => 'span3')));
}
private function getAllChildren($catID)
{
$choices = array();
$children = $this->doctrine->getRepository('KprCentarZdravljaBundle:Category')->findByParenting($catID);
foreach ($children as $child) {
$choices[$child->getId()] = $child->getName();
}
return $choices;
}
public function getName()
{
return 'category';
}
}
I am accessing the CategoryRepository function findByParenting($parent) from the CategoryType and I am getting the array populated with accurate data back from the function getAllChildren($catID) but the error is there, i think that Symfony framework is expecting an entity field instead of choice field, but dont know how to fix it.
I also changet the formCreate call in the controller giving $this->getDoctrine() as an argument to CategoryType():
$form = $this->createForm(new CategoryType($this->getDoctrine()), $cat, array('catID' => $id));
Ok i managed to resolve the dilemma. The answer was easy all I had to do is change
$builder->add('parent', 'choice', array(
'choices' => $this->getAllChildren($catID),
'required' => false,
'attr' => array('data-placeholder' => '--Izaberite Opciju--'),
));
to this:
$builder->add('parent', 'entity', array(
'class' => 'KprCentarZdravljaBundle:Category',
'choices' => $this->getAllChildren($catID),
'property' => 'name',
'required' => false,
'attr' => array('data-placeholder' => '--Izaberite Opciju--'),
));
And change the getAllChildren(..) function so that it returns objects
private function getAllChildren($catID)
{
$choices = array();
$children = $this->doctrine->getRepository('KprCentarZdravljaBundle:Category')->findByParenting($catID);
foreach ($children as $child) {
$choices[$child->getId()] = $child->getName();
}
return $choices;
}
I changed it to:
private function getAllChildren($catID)
{
$children = $this->doctrine->getRepository('KprCentarZdravljaBundle:Category')->findByParenting($catID)
return $children;
}
Lots of thanks to user redbirdo for pointing out the choices option on an entity field.
It seems like you are doing something too much complicated.
You are on the right way when you write Symfony framework is expecting an entity field instead of choice field.
To do this, replace:
$builder->add('parent', 'choice', array(
'choices' => $this->getAllChildren($catID),
'required' => false,
'attr' => array('data-placeholder' => '--Izaberite Opciju--'),
));
by:
$builder->add('users', 'entity', array(
'class' => 'KprCentarZdravljaBundle:Category',
'property' => 'name',
'query_builder' => function(EntityRepository $er) use($catID) {
return $er->findByParenting($catID);
},
'required' => false,
'empty_value' => '--Izaberite Opciju--'
));
(and you don't need getAllChildren($catID) anymore unless used somewhere else)
http://symfony.com/doc/current/reference/forms/types/entity.html
I'm building a custom validator that needs to validate the value from TWO form fields in the db in order to get this constraint to pass.
My question is this: the ContractValidator's validate method only has one $value in it's signature so how do I get access to the values from more than just a single field to do the validation?
Here is a typical custom validator:
namespace Acme\WebsiteBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class MyCustomValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
// check $value and return an error
// but in my case, i want the value from more than one form field to do a validation
// why? i'm checking that two pieces of information (ssn + dob year) match
// the account the user is registering for
}
}
Here's an example of a form class with some validations set:
namespace ACME\WebsiteBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\MinLength;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Regex;
use ACME\WebsiteBundle\Validator\Constraints\UsernameAvailable;
class AccountRegistration extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('ssn', 'number', array(
'max_length' => 9,
'required' => true,
'error_bubbling' => true)
);
$builder->add('year_of_birth', 'choice', array(
'choices' => range(date("Y") - 100, date("Y")),
'required' => true,
'empty_value' => 'Select ...',
'label' => 'Year of Birth',
'error_bubbling' => true)
);
$builder->add('username', 'text', array(
'required' => true,
'error_bubbling' => true)
);
$builder->add('password', 'password', array(
'max_length' => 25,
'required' => true,
'error_bubbling' => true)
);
$builder->add('security_question', 'choice', array(
'empty_value' => 'Select ...',
'choices' => array(),
'label' => 'Security Question',
'required' => true,
'error_bubbling' => true)
);
$builder->add('security_question_answer', 'text', array(
'label' => 'Answer',
'required' => true,
'error_bubbling' => true)
);
}
public function getName()
{
return 'account_registration';
}
public function getDefaultOptions(array $options)
{
$collectionConstraint = new Collection(array(
'allowExtraFields' => true,
'fields' => array(
'ssn' => array(new MinLength(array('limit' => 9, 'message' => 'too short.')), new NotBlank()),
'year_of_birth' => array(new NotBlank()),
'username' => array(new NotBlank(), new UsernameAvailable()),
'password' => array(new NotBlank(), new Regex(array(
'message' => 'password must be min 8 chars, contain at least 1 digit',
'pattern' => "((?=.*\d)(?=.*[a-z]).{8,25})"))
),
'security_question' => array(new NotBlank()),
'security_question_answer' => array(new NotBlank()))
)
);
return array(
'csrf_protection' => true,
'csrf_field_name' => '_token',
'intention' => 'account_registration',
'validation_constraint' => $collectionConstraint
);
}
}
Any custom validator that extends ConstraintValidator has access to the $context property. $context is an instance of ExecutionContext that gives you access to submitted data:
Example:
<?php
namespace My\Bundle\MyBundle\Validator\Constraints;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
class AppointorRoleValidator extends ConstraintValidator
{
public function validate($value, Constraint $constraint)
{
$values = $this->context->getRoot()->getData();
/* ... */
}
}
You need to use CLASS_CONSTRAINT as described in the cookbooks. You then get passed the whole class and can use any property of this class. In your example above, this would mean that instead of the $value beeing one string/integer, it would be the whole object you want to validate.
The only thing you need to change is getTargets() functions, which has to return self::CLASS_CONSTRAINT.
Also make sure that you define your validator on class level, not on property level. If you use annotations this means that the validator must be described above the class defnition, not above one specific attribute definition:
/**
* #MyValidator\SomeValidator
*/
class MyClass {
}
I created a FormType for a registration form. The validation work like it should. Strange thing is:
The errors get printed right away. When displayed (on load of the page), the form tells me right away that some fields are not valid, although I haven't started to fill in the fields at that point.
my formtype class:
<?php
namespace MyBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Validator\Constraints\Collection;
use Symfony\Component\Validator\Constraints\MaxLength;
use Symfony\Component\Validator\Constraints\MinLength;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
use MyBundle\Validation\Constraint\Unique;
use MyBundle\Validation\Constraint\InvitationCode;
use MyBundle\Validation\Constraint\Username;
class RegistrationFormType extends AbstractType
{
/**
*
* #var FormBuilder
*/
private $builder;
public function buildForm(FormBuilder $builder, array $options)
{
$this->builder = $builder;
$this->builder
->add('code','text', array(
'label' => 'Einladungscode'
))->add('username','text',array(
'label' => 'Benutzername',
))
->add('email', 'email',array(
'label' => 'E-Mail'
))
->add('plainPassword', 'repeated', array('type' => 'password',
'first_name'=>'Passwort',
'second_name'=> 'Passwort wiederholen',
)
);
}
public function showRegistrationFields(){
}
public function getDefaultOptions(array $options)
{
$collectionConstraint = new Collection(array(
'email' => array(
new NotBlank(),
new Unique(array(
'entityName' => 'AjadoEventHubBundle:User',
'propertyName' => 'email')),
new Email(array(
'message' => 'Ungültige E-Mail Adresse',
)),
),
'username' => array(
new NotBlank(),
new Unique(array(
'entityName' => 'AjadoEventHubBundle:User',
'propertyName' => 'username')),
new Username(),
new MinLength(array('limit' => 5)),
new MaxLength(array('limit' => 40)),
),
'code' => array(
new MaxLength(array('limit'=>200)),
new InvitationCode(),
),
'plainPassword' => array(
new MaxLength(array('limit'=>20)),
new MinLength(array('limit' => 5))
),
));
return array(
'csrf_protection' => false,
'validation_constraint' => $collectionConstraint,
);
}
public function getName()
{
return 'registration';
}
}
I changed
$form->bindRequest($this->getRequest());
if ($this->getRequest()->getMethod() == 'POST' && $form->isValid()) {
to
if ($this->getRequest()->getMethod() == 'POST') {
$form->bindRequest($this->getRequest());
if($form->isValid()){
Thanks #meze for the hint!