Symfony parameters Routes (Easy to answer) - symfony

I try to pass my user id in parameter because I wanted to use it in an other function to link them but don't know why that don't work (I think that I do a mistake and will be easy to answer thx :)
return $this->redirect('/profile/new/', array(
'id' => $user->getId(),
));
My receiver :
/**
* Creates a new profile entity.
*
* #Route("/new/{id}", name="profile_new")
*/
public function newProfileAction(Request $request)
{
when I do /profile/new/8 for example it works! But when I click in the button submit that don't redirect with the id ... (of course the routes are good and when I do - it works :
return $this->redirect('/profile/new');
my receiver (when it works) :
/**
* Creates a new profile entity.
*
* #Route("/new", name="profile_new")
*/
public function newProfileAction(Request $request)
{

You should use $this->redirectToRoute('ROUTENAME',[PARAMETERS]) means:
$this->redirectToRoute('profile_new',['id'=>$ID])
If you use $this->redirect('URL') you have to parse the URL so you need to "/profile/new/ID"

Related

Symfony - Doctrine Listener create url from name

can someone please explain, how to create listener to fill url field from name field without using existing slug libraries?
Example:
Name filed: this is an example #1
Url field: this-is-an-example-1
Now I have it in __constructor in entitiy... But I want to know how to use listeners... And I can't find example on google.
Thank you
You can try something like this :
/**
* #ORM\HasLifecycleCallbacks()
*/
class YourEntity
{
/**
* #ORM\PrePersist
*/
public function slugify()
{
$name = $this->getName();
$slug = functionToSlugify($name)
$this->setUrl($slug);
}
}
So you can create your own slugify function and before persisting your entity it will set your url for you.
That said, I highly recommend Doctrine Extension Bundle :
http://symfony.com/doc/current/doctrine/common_extensions.html
There's no point to reinvent the wheel ...

symfony symfony3 new single ManyToOne object ( owning side of the association )

I have Cases, which also have Attachments. This is coded as a Case entity with the OneToMany association to Attachment entity. The Attachment entity has a ManyToOne association to Case entity. The code:
class Case {
/**
* #var integer
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Attachment", mappedBy="case",cascade={"persist"})
*/
protected $attachments;
class Attachment
{
/**
* #var integer
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Case", inversedBy="attachments")
* #ORM\JoinColumn(name="case_id", referencedColumnName="id")
*/
protected $case;
}
Im trying the following. I pretend to show/open the whole case into a single page. Inside the page, there will be the attachment list. At the end of that list, I pretend to put a form for new attachment submissions.
So I have written a controller to show the case, and I have created a new attachment form (AttachmentType ) and place it in the middle of the twig template, passing it into the render call of the action as a argument.
// CaseController.php
/**
* #Route("/cases/show/{case}", name="showCase", requirements={ "case" : "\d+" } )
*/
public function showCaseAction(Request $request, $case)
{
$theCase = $this->getDoctrine()->getRepository('AppBundle:Case')->findOneById( $case );
$attachment = new Attachment();
$attachment->setCase( $theCase );
$attachmentForm = $this->createForm(AttachmentType::class, $attachment);
if ( ! $theCase ) {
$this->addFlash('danger', $this->get('translator')->trans('cases.show.case_not_found', [ '%case%' => $case ] ) );
}
return $this->render('cases/caseContainer.html.twig', array( 'case' => $theCase, 'attachmentform' => $attachmentForm->createView() ) );
}
And also I have written a newAttachmentAction into the controller to perform the attachment creation.
I stop writting my code here. I dont want to condition the possible answers.
My problem is that im not able to figure out how to recover the Case object when the newAttachmentAction is called, so I can do the association. I cant figure out if i should place something ( HiddenType,EntityType,etc ) into the Attachment Form Builder to store the Case object, or maybe would be better to use some other Symfony mechanism (Services, Closure, StorageTokens). I have made a wide search along the web, but i have read some many articles, that Im stucked ! Im probably missing the right search keywords.
I hope i have made my self clear, and therefore someone can point me into the right direction to find an example or a tutorial.
Best regards and many many thanks for your time and attention !
For creating a Case, i will add a HiddenType for attachment property inside the CaseformType.
Set data_class to Attachment
When you will create the form, you will pass a new instance of Case with the attachment reference.
After the post, when you will receive the form data, you will have the linked object

Dynamic number of Contact Form 7 recipients

I am working a on a form with, at it's core, two fieldset: main and "other recipient"; at the end of of the "other recipient" fieldset, I have a "add another recipient" link.
Here's who needs what:
Main recipient: everything
Other recipient: the "other recipient" fieldset;
Sub-sequent recipients: Respective fieldsets
So far, I've been looking at the Documentation but not much luck there, not that I expected any, either.
Edit
I think this is unclear, so I will be a little more explicit as to what is the context. My form is a registration where we can sign up multiple people; one of the fields is labeled "Your email". Since we can register more than one person at once, I need to duplicate the fieldset containing "Your email".
Edit 2
To help clarify, imagine that we are signing up kids for a summer camp. The first fieldset is general, say the parent's billing information, and the second fieldset is the child's information. The parent needs to be able to fill out a single form and dynamically add as many children as the parent desires.
In each of the children's fieldset, their email is required and they receive the information relevant to this child, where the email would be similar to:
Hello {children's name},
You've been registered to StackOverflow Summer Camp. Here's the information you need to know:
[...]
Thanks for being a good sport!
Hope this helps.
When you've got a specific use case like this, shoehorning the functionality into peripherally related plugins often results in frustration. That being said - there are times where you're married to a specific plugin or approach, and just have to build on top of it.
With that caveat out of the way, I think you should approach this from the angle of creating a new fieldtype for Contact Form 7. This way you have control over rendering the field's HTML, the data validation, among other things. It might also provide an launch point for DB storage and sending reminders, later, as you've mentioned in a comment on another answer.
Here's this approach in action:
The new fieldtype is called emailplus, and you include it into a form like this:
<div class="cf7-duplicable-recipients">
<label>Main Recipient (required)</label>
[emailplus emails]
[submit "Sign Up"]
</div>
Additionally, I've set the recipient under the "mail" panel in the form's settings to [emails].
If an emailsplus field is set as the recipient, the class overrides the default wpcf7 behaviour and sends mail to each value in the email array, substituting any [emails] placeholders in the body of the message on a per-email basis.
The emailplus fieldtype is encapsulated here in a class, and commented liberally:
<?php
class WPCF7_Duplicable_Email
{
/**
* The emails this form's field is addressed to
*
* #var array
*/
public $emails = array();
/**
* The name of the tag bearing the emailplus type
*
* #var string
*/
public $tag_name;
/**
* Instantiate the class & glom onto the wpcf7 hooks
*
* #return void
*/
public function __construct()
{
add_action('wpcf7_init', array($this, 'add_emailplus_tag'));
add_action('wpcf7_form_tag', array($this, 'assign_values_to_field'));
add_filter('wpcf7_validate_emailplus', array($this, 'validate'), 2);
add_action('wpcf7_before_send_mail', array($this, 'send_email'));
add_action('wp_enqueue_scripts', array($this, 'emailplus_scripts'));
}
/**
* Add our the [emailplus __YOUR_FIELD_NAME__] shortcode for use in wpcf7 forms.
*
* #uses wpcf7_add_shortcode
*
* #return void
*/
public function add_emailplus_tag()
{
wpcf7_add_shortcode(
array('emailplus'),
array($this, 'shortcode_handler'),
true
);
}
/**
* Renders the HTML for the [emailplus] shortcode
*
* Referenced from wpcf7_text_shortcode_handler in wp-content/plugins/contact-form-7/modules/text.php
*
* #param array $tag The data relating to the emailplus form field.
*
* #uses wpcf7_get_validation_error
* #uses wpcf7_form_controls_class
*
* #return string
*/
public function shortcode_handler(array $tag) {
$tag = new WPCF7_Shortcode($tag);
if (true === empty($tag->name)) {
return '';
}
$validation_error = wpcf7_get_validation_error($tag->name);
$class = wpcf7_form_controls_class($tag->type);
if ($validation_error) {
$class .= ' wpcf7-not-valid';
}
$atts = array(
'class' => $tag->get_class_option($class),
'id' => $tag->get_id_option(),
'tabindex' => $tag->get_option('tabindex', 'int', true),
'aria-invalid' => $validation_error ? 'true' : 'false',
'type' => 'email',
'name' => $tag->name.'[]', // <-- Important! the trailing [] Tells PHP this is an array of values
'value' => $tag->get_default_option()
);
$atts = wpcf7_format_atts($atts);
$html = sprintf(
'<div class="emailplus-wrapper %1$s"><input %2$s />%3$s</div>',
sanitize_html_class($tag->name),
$atts,
$validation_error
);
// We identify the container that will hold cloned fields with the [data-wpcf7-duplicable-email] attr
return '<div class="wpcf7-form-control-wrap %1$s" data-wpcf7-duplicable-email>'.$html.'</div>';
}
/**
* Validates the value of the emailplus tag.
*
* Must be handled separately from other text-based form inputs,
* since the submitted POST value is an array.
*
* We can safely assume emailplus while creating the WPCF7_Shortcode,
* because we know we hooked this function onto 'wpcf7_validate_emailplus'
*
* #uses wpcf7_is_email
* #uses WPCF7_Validation::invalidate
*
* #param WPCF7_Validation $result The validation helper class from wpcf7.
* #param array $tag The array of values making up our emailplus tag
*
*/
public function validate(WPCF7_Validation $result, $tag)
{
$tag = new WPCF7_Shortcode(
array(
'basename' => 'emailplus',
'name' => $this->tag_name,
'raw_values' => $this->emails
)
);
// Check each value of the form field.
// Emails must be validated individually.
foreach($tag->raw_values as $email) {
if (false === wpcf7_is_email($email)) {
$result->invalidate($tag, wpcf7_get_message('invalid_email'));
}
}
return $result;
}
/**
* For completeness' sake, manually assign the value to the emailplus fieldtype.
*
* Wpcf7 doesn't know how to treat our fieldtype's value by default.
*
* As a side effect, this sets the email addresses that are up for delivery.
*
* #param array $scanned_tag The tag that wpcf7 is scanning through, and processing.
*
* #return $array;
*/
public function assign_values_to_field($scanned_tag)
{
if ($scanned_tag['type'] !== 'emailplus') {
return $scanned_tag;
}
$this->tag_name = $scanned_tag['name'];
if (key_exists($scanned_tag['name'], $_POST)) {
// Stores the emails on a class property for use later.
$this->emails = $_POST[$scanned_tag['name']];
$scanned_tag['raw_values'] = $this->emails;
$scanned_tag['values'] = $this->emails;
}
return $scanned_tag;
}
/**
* Performs a substitution on the emailplus field's fieldname, on a per-value basis.
*
* Ex. in two parts
* 1 - The shortcode [emailsplus emails] renders into <input type="email" name="emails[]" value="" >
* Which the user clones and submits, processing into something like
* ['test1#gmail.com', 'test2#gmail.com'].
* 2 - The user has set [emails] as the recipient in the "mail" panel for the form.
*
* Because wpcf7 isn't aware of how to process a field w/ multiple values when emailing,
* we loop over the values of [emails], replace the tag, and invoke WPCF7_Mail::send()
* for each value.
*
* #param WPCF7_ContactForm $form The contact form object.
*
* #uses WPCF7_Mail::send
*
* #return void
*/
public function send_email(WPCF7_ContactForm $form)
{
$placeholder = '['.$this->tag_name.']';
if (false === strpos($form->prop('mail'), $placeholder)) {
return;
}
foreach ($this->emails as $email) {
$template = $form->prop('mail');
$template['recipient'] = str_replace($placeholder, $email, $template['recipient']);
$template['body'] = str_replace($placeholder, $email, $template['body']);
WPCF7_Mail::send($template);
}
// Tell cf7 to skip over the default sending behaviour in WPCF7_Submission->mail()
$form->skip_mail = true;
}
/**
* Adds our js that will clone the emailplus field.
*
* Could be optimized with a conditional that checks if there is a form with the [emailplus]
* fieldtype somewhere on the page
*
* #return void
*/
public function emailplus_scripts()
{
wp_enqueue_script(
'cf7-duplication',
get_template_directory_uri() . '/js/cf7-duplication.js',
array('jquery'),
'20161006',
true
);
}
}
$wpcf7DuplicableEmail = new WPCF7_Duplicable_Email();
And, the .js file that handles the cloning. It should live in /path/to/your/theme/js/cf7-duplication.js'.
(function($) {
$(document).ready(function() {
var addEmailField = function(inputRow, container) {
inputRow.find('input[type=email]').val('');
var removeButton = $('×')
.click(function(e) {
e.preventDefault();
inputRow.remove();
});
inputRow.append(removeButton);
inputRow.insertAfter(container.find('.emailplus-wrapper').last());
}
$.each($('[data-wpcf7-duplicable-email]'), function(i, el) {
var container = $(el);
var inputRow = container.find('.emailplus-wrapper');
var addButton = $('Add another email');
addButton.click(function(e) {
e.preventDefault();
var newEmailField = inputRow.clone();
addEmailField(newEmailField, container);
});
container.append(addButton);
});
});
})(jQuery);
Last, but not least, if you'd like the form to fadeout when it's valid and the emails have gone out, add this to the "additional settings" panel.
on_sent_ok: "jQuery('.cf7-duplicable-recipients').fadeOut();"
This approach is best for CF7's AJAX submissions. If you want to extend it to handle vanilla POST requests, you could update the shortcode handler to render multiple <input> fields where you need to preserve the value attr on invalid submissions.
Options
1) Under the Mail tap in the setup menu, after you click Mail(2), in the TO: field add this line. Should the parent have less than the number of kids stated, the extra email addresses should do nothing. [email],[email] is the basic format.
[parents-email], [kid-email1], [kid-email2], [kid-email3], [kid-email4], [kid-email5], [kid-email6], [kid-email7]
Option 2) In the TO: field, just place 1 email address such as the parents. and in the Additional Headers: place the code below
CC: [kid-email1], [kid-email2], [kid-email3], [kid-email4], [kid-email5], [kid-email6], [kid-email7]
or
BCC: [kid-email1], [kid-email2], [kid-email3], [kid-email4], [kid-email5], [kid-email6], [kid-email7]
A possible problem that could arise: Many hosts block things like this to prevent spam. If theses do not work, then this is probably the case. You would need to contact your hosting provider about removing the block.
Contact form 7 has only one recipient field, however you can enter multiple email address with comma separated in that field, example "email1#domain.com,email2#domain.com,email3#domain.com".
So for your case, use JavaScript to add multiple duplicate recipient fields dynamically, and finally on form submit write a JavaScript function to concat all the recipient email addresses then keep that in main recipient field and submit the form. Hope you got my point.

how to pass variable from twig path to the same controller?

In template i need to pass variable to controller when clicked link
{{ variable }}
click
.
How to do this?
/**
* #Route("/test", defaults={"variable" = 1}, name="test")
* #Method("GET")
* #Template()
*/
public function testAction($variable)
{
return array('variable'=>$variable);
}
You will say i need placeholder in #Route /test/{variable}, then how to first time visit url test?
edit: this is silly question. I had some cache problem while testing this issue. The answear is obvious.
You need to define your #Route annotation like you mention:
/**
* #Route("/test/{variable}", defaults={"variable" = 0}, name="test")
* #Method("GET")
* #Template()
*/
public function testAction($variable)
{
return array('variable'=>$variable);
}
Thanks to defaults option you can access your route with or without variable:
With:
click
This will generate url /test/2 and your $variable will equal 2
Without:
click
This will generate url /test and your $variable will equal 0 (a value set in defaults option)
I had to do something similar, and #Tomasz, your answer helped me a lot. I my case I needed two variables.
Using the above example as a reference:
* #Route("/test/{variable}/{var2}",
* defaults={"variable" = 0, "var2" = 0},
* name="test")
* #Method("GET")
* #Template()
*/
public function testAction($variable, $var2)
{
return array('variable'=>$variable, 'var2 => $var2);
}
Then in twig you can use:
click
which generates URL /test/2/3
In my case, I was using something more fancy, like an Entity:
<td><a href="{{ path('submitPetHasProgram',
{'prog':stu.getTcProgram.getProgramId,
'student':stu.getStuId}) }}">Select</a></td>
Hopefully this will help someone out in the future who is struggling for a solution to this type of problem.

Symfony Newb Routing Issue

I have just started using Symfony and I am having a routing problem. Here is the routing fromt the controller:
/**
* #Route("/social/{name}/", name="_speed1")
* #Route("/social/drivers/")
* #Route("/social/drivers/{name}/", name="_driver")
* #Route("/social/", name="_speed")
* #Template()
*/
public function unlimitedAction()
{
If I go to speed/social/ or speed/social/bob or speed/social/drivers/ or speed/social/drivers/bob all of those pages render with no problem. However I need the name being passed in so I changed
public function unlimitedAction()
{
to
public function unlimitedAction($name)
{
If I go to speed/social/drivers/ or speed/social/drivers/bob it returns fine. However, if I go to speed/social/ then I get the following error:
Controller "MyBundle\Controller\DefaultController::unlimitedAction()"
requires that you provide a value for the "$name" argument (because there is
no default value or because there is a non optional argument after this one).
I can't understand why it works for one route but not the other.
So my question is, how can I acheive my routing so that I can go to:
speed/social/
speed/social/drivers/
speed/social/drivers/bob
And be able to pass the variable to the action without error.
Thanks!
To answer your question: you have to provide a default value for name parameter, for each route without the {name} parameter in the url. I can't test it right now and I can't remember the syntax when using annotations, but should be something like this:
/**
* #Route("/social/{name}/", name="_speed1", defaults={"name"=null})
* #Route("/social/drivers/{name}/", name="_driver", defaults={"name"=null})
* #Template()
*/
public function unlimitedAction($name)
{
}
This way you should be able to call /social/ and /social/foo as well as /social/drivers/ and /social/drivers/foo.
But, really, this is not the right way to go. Just define more actions, each binded to a single route:
/**
* #Route("/social", name="social_index")
* #Template()
*/
public function socialIndexAction() { } // /social
/**
* #Route("/social/{name}", name="social_show")
* #Template()
*/
public function socialShowAction($name) { } // /social/foo
As a general rule, each method (each action) should be focused to do just one thing and should be as short as possible. Use services and make your controllers do what they are supposed to do: understand user input, call services and show views.

Resources