send sms on symfony and api ovh - forms

I have a problem when I try to pass my phone numbers in a form to send sms with api ovh, the phone numbers on the outside of the form I can get them back but inside it sends me a null.
Thank you for the help :
public function sendSmsAction(Request $request)
{
$listphoneNumber = $request->get('telephone');
var_dump($listphoneNumber); // it returns an array phone numbers
try{
$form = $this->createForm(smsFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$message = $form->get('message')->getData();
$smsProvider = $this->get('app.sms.provider');
var_dump($listphoneNumber); // it returns null
$smsProvider->sendMessage($message, $listphoneNumber);
}
} catch (InvalidParameterException $e) {
sprintf("Erreur lors de l'envoie de SMS, il faut choisir un utilisateur : %s . Trace : %s", $e->getMessage(), $e->getTraceAsString()
);
throw $e;
}
return $this->render('CeUtilisateurBundle:Utilisateur:sms.html.twig', array(
'form' => $form->createView()));
}

I solved the problem
Controller
public function sendSmsAction(Request $request)
{
try{
$form = $this->createForm(smsFormType::class, array('telephone' => $request->get('telephones')));
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$message = $form->get('message')->getData();
$smsProvider = $this->get('app.sms.provider');
$phoneNumbers=$form->getConfig()->getData();
$smsProvider->sendMessage($message, $phoneNumbers);
}
} catch (InvalidParameterException $e) {
sprintf("Erreur lors de l'envoie de SMS, il faut choisir un utilisateur : %s . Trace : %s", $e->getMessage(), $e->getTraceAsString()
);
throw $e;
}
return $this->render('CeUtilisateurBundle:Utilisateur:sms.html.twig', array(
'form' => $form->createView()));
}
formType
class smsFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('telephone', HiddenType::class, array(
'data' => $options['telephone'],
))
->add('message', TextareaType::class, array(
'attr' => ['class' => 'tinymce',
'placeholder' => 'Votre message',
],
'label' => false,
'required' => true))
->add('Envoyer', SubmitType::class, array(
'attr' => array(
'class' => 'btn btn-primary',
)
));
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'telephone' => null
));
}
}

Related

Functionnals tests with Symfony4 on Forms (EntityType input) didn't work

I have a form for Product Entity
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['edit'] === false) {
$builder
->add('code', TextType::class, [
'label' => 'Code',
])
->add('name', TextType::class, [
'label' => 'Nom',
]);
}
$builder
->add('competence', EntityType::class, [
'label' => 'Compétence',
'choice_label' => 'name',
'class' => Competence::class,
'required' => false,
]);
}
It's works but when I want to write functionnal test it didn't work. While my unit tests are working.
For testing, i've a database AND database_test. For this I use the .env.local and .env.test.local with the right DATABASE_URL (main and test).
public function testAdd()
{
// Instance du client HTTP
$client = static::createHttpBrowserClient();
// Construction de la requête GET sur la page product
$crawler = $client->request(
'GET',
getenv('APP_TEST_URL') . '/product/new'
);
$form = $crawler->selectButton('Ajouter')->form([
'product[name]' => 'Product Test',
'product[code]' => 'ABC_DEF',
'product[competence]' => $this->entityManager->getRepository(\App\Entity\Competence::class)->findOneBy(['name' => 'BVB'])->getId(),
]);
$client->submit($form);
$crawler = $client->followRedirect();
$this->assertSame(1, $crawler->filter('div.alert.alert-success')->count());
}
How I setup this :
/**
* #var \Doctrine\ORM\EntityManager
*/
private $entityManager;
public function setUp(): void
{
$kernel = self::bootKernel();
$this->entityManager = $kernel->getContainer()
->get('doctrine')
->getManager();
}
public function tearDown(): void
{
$client = static::createHttpBrowserClient();
$client->restart();
}
My problem is about the EntityType Competence. I've to put the Id that I want but for the same Competence in Database and Database_URL, ids are not the same.
So I get this error :
App\Tests\E2e\Controller\ProductControllerTest::testAdd
InvalidArgumentException: Input "product[competence]" cannot take "3" as a value (possible values: "", "258", "259", "260", "261", "262", "263", "264", "265", "266", "267", "268", "269", "270", "271", "272", "273", "274", "275", "276", "277", "278", "279").
Even if I enter "258", for example, I've this error :
"LogicException: The request was not redirected." on the followRedirect().
I don't know if I'm on the right way, but I'm lost right now. I tried a lot of things with doctrine.yaml and .env.*

symfony validation error on nested form for dynamically modified field

I have an embedded forms with a entity field Activity in a sub form dynamically populated with data from a field Module whose data are set via ajax request.
But when i submit the form i have a validation error ("this value is not valid") and the activity field is blank.
The principle :
the main form (Session) has an entity field module and contains a collection (Timeslot).
The Timeslot subform has a field activity whose content depends on the Module value.
My forms :
class SessionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('module', EntityType::class, [
'label' => 'module.name',
'required' => true,
'class' => Module::class,
'placeholder' => 'choose',
])
->add('timeslots', CollectionType::class, [
'required' => true,
'constraints' => new Valid(),
'prototype_name' => '__timeslot_prot__',
'entry_type' => TimeslotType::class,
'entry_options' => ['module' => isset($module) ? $module->getId() : 0],
'by_reference' => false,
'allow_add' => true,
'allow_delete' => true,
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Session::class,
'module' => null,
]);
}
}
And the Timeslot
class TimeslotType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$module = $options['module'];
$builder
->add('activity', EntityType::class, [
'class' => Activity::class,
'multiple' => false,
'required' => true,
'constraints' => new NotBlank(),
'query_builder' => function (ActivityRepository $activity) use ($module) {
if (null !== $module) {
$qb = $activity->createQueryBuilder('a')
->join('a.activityGroups', 'ag')
->join('ag.module', 'm')
->where('m.id = :mid')
->setParameter('mid', $module)
->orderBy('a.name', 'ASC');
return $qb;
} else {
$qb = $activity->createQueryBuilder('a')
->where('a.id = 0');
return $qb;
}
},
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Timeslot::class,
'module' => null,
]);
}
}
I also have a javascript script which fills the Activity select options with a query through an ajax call.
I also tried to modify my TimeslotType following this symfony doc
but i stumble upon the problem that in my case, the Module field is not in the same formBuilder than Activity, so the POST_SUBMIT eventlistener can't be applied in my case.
class TimeslotType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$module = $options['module'];
$formModifier = function (FormInterface $form, Module $module = null) {
$activities = [];
if (null !== $module){
foreach($module->getActivityGroups() as $ag){
foreach($ag->getActivities() as $activity){
$activities[] = $activity;
}
}
}
$form->add('activity', EntityType::class, [
'label' => 'timeslot.activity',
'label_attr' => ['class' => 'mandatory', 'data-extrainfo' => 'panel_timeslot'],
'attr' => [
'class' => 'activitieslist',
],
'class' => Activity::class,
'multiple' => false,
'required' => true,
'constraints' => new NotBlank(),
'choices' => $activities,
'choices_as_values' => true,
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// the entity : Timeslot
$data = $event->getData();
$formModifier($event->getForm(), $module);
}
);
//$builder->get('module') ... Makes no sense here since the module attribute is not a Timeslot, but belongs to Session
/*
$builder->get('module')->addEventListener(FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$module = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $module);
}
);
*/
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Timeslot::class,
'module' => null,
]);
}
}
How can i resolve my problem and validate my form ?
===
Edit
The ajax request is called when the module field is changed
function updateActivities(block) {
var module = $('#session_module').val();
if (module != ''){
$.ajax({
url: Routing.generate('project_module_activities_ajax', {}),
data: { 'module': module },
method: 'POST',
success: function (activities) {
var sel = '';
for (var i = 0; i < activities.length; i++) {
sel += '<option value="' + activities[i].id + '">' + activities[i].name + '</option>'
}
$('#litimeslot'+block).find('.activitieslist').each(function () {
$(this).html(sel);
})
}
});
}
}
And the route project_module_activities_ajax simply returns an array of activities for the module from a query
something like :
[
['id' : 1, 'name' : 'activity 1'],
['id' : 2, 'name' : 'activity 2'],
}
Edit 2
if i add this eventlistener in the TimeslotType :
$builder->addEventListener(FormEvents::PRE_SUBMIT,
function (FormEvent $event) use ($moduleId) {
$form = $event->getForm();
$data = $event->getData();
echo '<pre>form AFTER=';var_dump($form->getData());echo '</pre>';
echo '<pre>data AFTER=';var_dump($data);echo '</pre>';
echo '<pre>module AFTER';var_dump($moduleId);echo '</pre>';
}
);
I have
form AFTER= NULL
data AFTER= array(
["activity"]=>
string(3) "573" <= which is the value i selected
...
)
module AFTER int(0)

Symfony 2.8 - Set default value with data from session and propel

I use Propel in a project & I try to set default value my form which uses a ModelType input and I need to set a default value stored in session in this form and where this session is not null for the stored value used in this functionnality.
This is my form :
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Fcl\VitrinellisBundle\Model\ProfileVariety',
'name' => 'profile_variety_search',
'locales' => ['fr'],
'session' => null
));
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', ModelType::class, array(
'class' => 'Fcl\VitrinellisBundle\Model\ProfileVariety',
'query' => ProfileVarietyQuery::create()->orderById(),
'property' => 'name',
'label' => 'Profil recherché',
'expanded' => false,
'multiple' => false,
'required' => false,
'placeholder' => '- Filtrer par profil -',
'attr' => array(
'onchange' => 'submit()',
'class' => 'col s3'
)
))
;
}
This is my treatment :
public function listAction(Request $request = null)
{
$pModelManager = $this->get('fcl_vitrinellis.p_model_manager');
$profileVarietyManager = $this->get('fcl_vitrinellis.profile_variety_manager');
$session = $request->getSession();
$profileVariety = new ProfileVariety();
$models = null;
$form = $this->createForm(ProfileVarietySearchType::class, $profileVariety);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (null != $form['name']->getData()) {
$models = $pModelManager->getWebsiteByModel($form['name']->getData()->getName());
$session->set('profileVarietySearch', $form['name']->getData()->getName());
} else {
$models = $pModelManager->getList();
}
} else {
if ($session->has('profileVarietySearch') && null != $session->get('profileVarietySearch')) {
$models = $pModelManager->getWebsiteByModel($session->get('profileVarietySearch'));
} else {
$models = $pModelManager->getList();
}
}
return $this->render('console\p_model_list.html.twig', array(
'objArray' => $models,
'form' => $form->createView()
));
}
I have try to set default data with 'data' option and with PRE_SET_DATA event in the form but I had satisfactory result.
In an EntityType the incoming default 'data' must be an object of the right type. I would first try to see if we have incoming form data.
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Check form data
$formDataEntity = $builder->getData();
// Check if it has the field filled in
if ($formDataEntity && $formDataEntity->getName()) {
$objToSet = $formDataEntity->getName();
} else {
$objToSet = $options['incomingDefaultObject'];
)
$builder
->add('name', ModelType::class, array(
class => 'Fcl\VitrinellisBundle\Model\ProfileVariety',
data => $objToSet,
...
And then for the resolver
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'incomingDefaultObject' => null,
));
}
And you call the form with the default opion in the controller
$form = $this->createForm(YourType::class, $yourObject, array('incomingDefaultObject' => $nameObject));
Warning: if a user decides to leave the field empty this code will always set the default.
I have an other solution.
Create new Model which his name is ProfileVarietySearch, like this :
class ProfileVarietySearch
{
/** #var null|ProfileVariety $profileVariety */
private $profileVariety;
/**
* #return bool
*/
public function is_empty()
{
return is_null($this->profileVariety);
}
/**
* #return null|ProfileVariety
*/
public function getProfileVariety()
{
return $this->profileVariety;
}
/**
* #param $profileVariety
*
* #return ProfileVarietySearch
*/
public function setProfileVariety($profileVariety): self
{
$this->profileVariety = $profileVariety;
return $this;
}
}
In the controller, write this :
public function listAction(Request $request = null)
{
$pModelManager = $this->get('fcl_vitrinellis.p_model_manager');
$profileVarietyManager = $this->get('fcl_vitrinellis.profile_variety_manager');
$session = $request->getSession();
$profileVarietySearch = new ProfileVarietySearch();
$models = null;
if ($session->has('profileVarietySearch') && null != $session->get('profileVarietySearch')) {
$profileVarietySearch->setProfileVariety(
$profileVarietyManager->getByName($session->get('profileVarietySearch'))->getData()[0]
);
}
$form = $this->createForm(ProfileVarietySearchType::class, $profileVarietySearch);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (null != $form->getData()->getProfileVariety()) {
$models = $pModelManager->getWebsiteByModel($form->getData()->getProfileVariety()->getName());
$session->set('profileVarietySearch', $form->getData()->getProfileVariety()->getName());
} else {
$session->set('profileVarietySearch', null);
$models = $pModelManager->getList();
}
} else {
if ($session->has('profileVarietySearch') && null != $session->get('profileVarietySearch')) {
$models = $pModelManager->getWebsiteByModel($session->get('profileVarietySearch'));
} else {
$models = $pModelManager->getList();
}
}
return $this->render($this->view_list, array(
'objArray' => $models,
'form' => $form->createView()
));
}
In the ProfileVarietySearchType, write this :
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Fcl\VitrinellisBundle\Form\Model\ProfileVarietySearch',
'name' => 'profile_variety_search'
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('profileVariety', ModelType::class, array(
'class' => 'Fcl\VitrinellisBundle\Model\ProfileVariety',
'query' => ProfileVarietyQuery::create()->orderById(),
'property' => 'name',
'label' => 'Profil recherché',
'expanded' => false,
'multiple' => false,
'required' => false,
'placeholder' => '- Filtrer par profil -',
'attr' => array(
'onchange' => 'submit()',
'class' => 'col s3'
)
));
}

Symfony2 : Save image form field in session

I want to save the form data in session in case of an error occured then I can display valid form data in my form view.
I've got a file field for an image but I've got this error when I submit my form with an image : Serialization of 'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed
My Form Type :
class ContenuType extends AbstractType
{
private $session;
public function __construct($session)
{
$this->session = $session;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$session = $this->session;
$formSession = $session->get('dataCreateContenu');
if (!$formSession) {
$formSession = new Contenu();
}
$builder
->add('description', 'textarea', array(
'label' => 'Description',
'data' => $formSession->getDescription(),
'attr' => array('maxlength' => 560),
)
)
->add('file', 'file', array(
'label' => 'Image',
'data' => $formSession->getImage(),
'required' => false,
)
)
;
}
}
My Form Controller :
/**
* New Contenu
*
* #Route("/", name="contenu_create")
* #Method("POST")
* #Template("MyOwnBundle:Contenu:new.html.twig")
*/
public function createAction(Request $request)
{
$entity = new Contenu();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
$session = $this->get('session');
$session->set('dataCreateContenu', $form->getData());
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$formAll = $form->all();
foreach ($formAll as $formField) {
if(empty($formField->getData())) {
$this->get('session')->getFlashBag()->add('error', 'Field « '.$formField->getConfig()->getOption("label").' » is empty.');
return $this->redirect($this->generateUrl('contenu_new'));
}
}
$image = $form->get('file')->getData();
if ($image) {
$entity->upload();// Upload
}
$em->persist($entity);
$em->flush();
$session->clear();
return $this->redirect($this->generateUrl('contenu_show', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
What can I do ?

Symfony2 - Adding Swiftmailer as a service

I'd like to move my email code from my controller into a service.
I've done the following thus far:
created the entry in services.yml
created a EmailManager.php file inside acme/demobundle/services/EmailManager.php
Could use some help on what needs to go into the EmailManager.php and how to call it in the controller?
services.yml
services:
email_manager:
class: Acme\DemoBundle\Services\EmailManager
arguments: [#request_stack, #mailer]
scope: request
EmailManager.php
<?php
// src/Acme/DemoBundle/Services/EmailManager.php
namespace Acme\DemoBundle\Services;
class EmailManager
{
private $mailer;
private $request;
public function __construct(RequestStack $requestStack, $mailer)
{
$this->request = $requestStack->getCurrentRequest();
$this->mailer = $mailer;
}
What needs to go here? Do I just copy/paste the code from the contactAction below into here?
}
Controller code with contactAction that I would like to move out of the controller into EmailManager service:
/**
* #Route("/", name="contact")
* #Template("AcmeDemoBundle:Default:index.html.twig")
*/
public function contactAction(Request $request)
{
$form = $this->createForm(new ContactType());
if ($request->isMethod('POST')) {
$form->submit($request);
if ($form->isValid()) {
$message = \Swift_Message::newInstance()
->setSubject($form->get('subject')->getData())
->setFrom($form->get('email')->getData())
->setTo('example#gmail.com')
->setBody(
$this->renderView(
'AcmeDemoBundle:Default:index.html.twig',
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()
);
}
ContactType Form
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' => 90,
'rows' => 10,
'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';
}
}
You can customize this as you see fit, but that's a general idea and a very quick draft to guide you:
public function send($subject, $recipientName, $recipientEmail, $bodyHtml, $bodyText)
{
/* #var $mailer \Swift_Mailer */
if(!$this->mailer->getTransport()->isStarted()){
$this->mailer->getTransport()->start();
}
/* #var $message \Swift_Message */
$message = $this->mailer->createMessage();
$message->setSubject($subject);
$message->setBody($bodyHtml, 'text/html');
$message->addPart($bodyText, 'text/plain', 'UTF8');
$message->addTo($recipientEmail, $recipientName);
$message->setFrom( array('example#gmail.com' => 'Chance') );
$this->mailer->send($message);
$this->mailer->getTransport()->stop();
}
Room for Improvement
You could have:
An email data model that would contain the fields necessary for an email (like $subject, $recipientEmail, ...)
A composer that would compose your email from your request
A sender that would send your email
EMAIL MODEL would look something like this:
/**
* Email Data Model
*/
class Email implements EmailInterface
{
/**
* The text part of the message.
*
* #var string
*/
protected $bodyText;
// etc...etc..
}
You'd have an EmailInterface too:
/**
* Email interface
*/
interface EmailInterface
{
/**
* #return string
*/
public function getBodyText();
// etc...etc..
}
THE SENDER would look like this (if kept inside EmailManager):
public function send(EmailInterface $email)
{
//...
}
THE COMPOSER would look like this (if kept inside EmailManager):
public function composeEmail(Request $request)
{
//...
return $email;
}
Note: Composer and Sender could also be a separate service for better reuse, that's up to you I guess. Here is what they would look like if there were just functions in your EmailManager