Symfony2 : How to upload a file without Doctrine? - symfony

I use Symfony2.3. I have a form like this :
<form action="{{ path("member_update") }}" method="post" {{ form_enctype(form) }}>
{{ form_widget(form.pic) }}
...
{{ form_widget(form._token) }}
</form>
and i want to upload user pictures in a directory.Then i use this in controller :
$member , $form , $dm is defined...
if ($form->isValid()) {
// Handle profile picture upload process
$uploadDir=dirname($this->container->getParameter('kernel.root_dir')) . '/web/bundles/mybundle/myfiles';
$form['pic']->getData()->move($uploadDir,$member->getId());
// End of upload
$dm->persist($member);
$dm->flush();
return $this->redirect($this->generateUrl("member_profile"));
}
It must work,but i see this error:
Exception: Serialization of 'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed
1. in pathToMyProject...\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\DataCollector\DataCollector.php line 27
2. at serialize(.....
What's the problem??!

The problem solved! I change this line in MemberType :
$builder->add('pic','file');
to this :
$builder->add('pic','file', array('mapped'=>false));
My mistake was that i must explain the "pic" field is not mapped to the Entity(or Document in Mongo,as my project). Else Symfony kernel try to put the value of "pic" in a field of Entity. And i have not any field that hold a file! I upload the picture in a directory and store only path to the picture within the entity. When i changed this,the problem solved easily! :-)
So keep in mind to explain all things clearly to Symfony!

Related

Cannot render text filtered with trans in twig template using Sandbox Security Policy

I am a little bit confused. First of all, look at my code, I guess.
public function renderTemplate($templateType, $data)
{
$layoutName = "$templateType.layout.html.twig";
$policy = new \Twig_Sandbox_SecurityPolicy(
['if', 'for', 'block', 'set', 'extends'],
['escape', 'format', 'dateformat', 'trans', 'raw', 'striptags'],
self::$allowedMethods,
self::$allowedProperties,
['gettext']
);
$sandboxExt = new \Twig_Extension_Sandbox($policy);
$intlExt = new \Twig_Extensions_Extension_Intl();
$i18nExt = new \Twig_Extensions_Extension_I18n();
$twig = new \Twig_Environment(new \Twig_Loader_Filesystem(__DIR__ . "/../Resources/views/Something/", "__main__"));
$sandboxExt->enableSandbox();
$twig->addExtension($sandboxExt);
$twig->addExtension($intlExt);
$twig->addExtension($i18nExt);
try {
$result = $twig->render($layoutName, $data);
} catch (\Exception $e) {
\Doctrine\Common\Util\Debug::dump($e);die();
}
return $result;
}
Here it is the template I want to render
{% extends 'layout.html.twig' %}
{% block title %}{{ entity.id }}{% endblock %}
{% block bodyTitle %}
{{ entity.id }} {{ 'translation_key.created_at'|trans({}, 'entities', locale) }} {{ entity.createdAt|dateformat(null, locale) }}
{% endblock %}
Here, as you can see, I want to render a template according to its type. The problem is: half of the template renders just fine, and then, when it tries to render the translated string, it throws an error.
Fatal error: Call to undefined function gettext() in /home/dev/vhosts/my-project/vendor/twig/twig/lib/Twig/Environment.php(403) : eval()'d code on line 69
I checked if this function existed right before trying to call render method, and it was really undefined. Basically, I have 2 questions here:
Question 1
How does it work in other parts of my project, but not in this specific handler? See "Important update" below.
Question 2
Can I solve my problem in another way? For example, without using Sandbox or using Sandbox with some kind of a flag "everythingAllowed=true"?
ATTENTION! IMPORTANT UPDATE
Previously, I misunderstood my own question. I thought the error was thrown while rendering the variable, but I re-checked the situation (When Alain Tiemblo asked me for twig template code here in comments) and now 100% sure it's thrown while trying to translate smth. Also, I have translations all over my project, and it works just fine, but in this specific situation it's not. I think it worth to mention, that I also tried to render the template without using Sandbox. I tried to render it directly from Twig Engine like this
return $this->templating->render($layoutName, $data);
//$this->templating is injected in the constructor via services.yml like this
//arguments:
// - "#templating"
The result - not properly translated text. When I dumped "locale" - I got one specific language, but the text was translated in another. But at least using this method - I didn't get any errors.. Can anybody clarify this for me? Because I really don't understand how the Intl/i18n extension works and why it doesn't want to work in Sandbox or not in Sanbox?
P.S. My guess, why it doesn't work directly from Twig Engine - probably I should inject not like "#templating" or it injects just right, but the Intl or I18n extensions are not enabled? How to enable. And I have no clues why it doesn't work with Sandbox

How can I unmap a File input without using the form builder?

I have inherited an application that seems to have existed before the form builder existed. The developer kind of rolled his own with twig macros. Now I want to add a file upload to some existing forms, but I get what seems to be a well known error:
There was 1 error:
myBundle\Tests\Controller\snip\DefaultControllerUnitTest::testCanStoreDocumentAtS3
Exception: Serialization of
'Symfony\Component\HttpFoundation\File\UploadedFile' is not allowed
C:\apath\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\DataCollector\DataCollector.php:27
C:\apath\vendor\symfony\symfony\src\Symfony\Component\HttpKernel\Profiler\Profiler.php:218
The solutions is to unmap the file field:
$builder->add('pic','file');
to this :
$builder->add('pic','file', array('mapped'=>false));
But in this case a builder is not used. Instead it looks like this:
{# file(name, value) #}
{% macro file(name, value) %}
<input type="file" name="{{ name }}" id="{{ name }}" value="{{ value }}" />
{% endmacro %}
Is there anything I can add to this macro, or do in the controller action to keep the Profiler from serializing this?
The answer was in part pilot error, but there is enough going on here that I hope I can help others avoid the same pitfall.
This error was triggered by a unit test where I was simulating a file upload along with other parameters. Initially the code that caused this error was:
$photo = new UploadedFile(
__DIR__ . '/TestReportMethod.xlsx',
'TestReportMethod.xlsx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
123
);
....
$form['reportFile'] = $photo;
//submit
This caused the error in my original post.
After trying a few things I moved to this form instead.
$crawler = $this->client->request('POST',
$url,
array('id' => 328),
array('agencyInfoId' => 328),
array('reportFile' => $photo)
);
Which causes an InvalidArgumentException I think a little better example in the cookbook could have prevented. The correct form needs to be an array of parameter values, followed by an array of files:
$crawler = $this->client->request('POST',
$url,
array('id' => 328,
'agencyInfoId' => 328),
array('reportFile' => $photo)
);
I hope this helps somebody else!

How to pass data from twig to Symfony2 controller using form

I would like to know if there is a method to pass some variables (text from textarea) from Twig to Symfony2 controller via form.
<form action="{{ path('admin_core_save') }}" method="post">
<div id="edit-template">
{% if template.getData() is defined %}
<textarea id="template">{{ template.getData() }}</textarea>
{% endif %}
</div>
<input type="submit" value="Save" />
</form>
When I press save button it is going to saveAction() method
public function saveAction(Request $request)
{
var_dump($request);
return new Response('abc');
}
but response does not contain any textarea text. Is there a way to get this there?
I know I can build form inside the controller and send it to Twig, but I would like to know if this way is possible.
you can access POST values through request object like:
$this->get('request')->request->get('name');
I'm sure you have to learn a bit about Symfony2 Form Component. You will find that symfony already has built-in system for rendering forms handling user data posted through them.
Answering your question. There is a Request object that provides you full access to all request data, including POST variables.
To access POST values use Request::get() method:
$request->get('variable_name');
To pass any data to the twig template, use TwigEngine::renderResponse
$this->container->get('templating')->renderResponse('AcmeDemoBundle:Demo:template.twig,html',
array(
'someVar' => 'some value'
)
);
This var will be avaliable in your template as:
{{ someVar }}

symfony2 CSRF invalid

Okay, so today I updated my database with new information from our 'live' database... And since then I've been having issues on one of my forms. If you need any code let me know and i'll edit this and post the code needed...
I have a report form which has a date range field and a drop down for an agent department. When I first visit the page I see this at the beginning of the form:
The CSRF token is invalid. Please try to resubmit the form
So I go over to one of my other forms that has the same type of information, and check the _token out and this is what comes out:
<input type="hidden" id="ecs_crmbundle_TimeClockReportType__token" name="ecs_crmbundle_TimeClockReportType[_token]" value="87e358fbc4d6d3e83601216b907a02170f7bcd92" />
<input type="hidden" id="ecs_crmbundle_SimpleSalesReportType__token" name="ecs_crmbundle_SimpleSalesReportType[_token]" value="87e358fbc4d6d3e83601216b907a02170f7bcd92" />
The first one is the one that shows the error, and the SimpleSalesReport does not... Any idea why this is doing this or how I can fix it?
Thanks..
Are you by chance using $form->bindRequest() in the action which produces the CSRF error? I had this issue. You should not be binding the request for a new form. If you are posting the form to the same action, wrap the bindRequest in a conditional which checks if method is POST:
if ($this->getRequest()->getMethod() == 'POST') {
$form->bindRequest($this->getRequest());
if ($form->isValid()) {
...
}
}
There is no problem using {{ form_widget(form) }} to build your custom form.
All you have to do is add the _token like this:
{{ form_widget(form._token) }}
This error had me crazy for days!
Thanks krishna!
If in your form template you choose to not use the default form behavior {{ form_widget(form) }} you SHOULD put {{ form_rest(form) }}
Hope this could help anyone else!

Extending existing Class in Symfony

I am new to symfony. I have created a registration form using the code:
$user = new Register();
$form = $this->createForm(new RegisterType(), $user);
In the RegisterType class i have 5 fields (for example).I store the values in database when the user registers with the system. Now I display the EDIT page using following code:
$user = $em->getRepository('MysiteUserBundle:Register')->find($id);
$form = $this->createForm(new RegisterType(), $user);
//edit.html.twig code
<form action="{{ path('MysiteUserBundle_register_update',{'id':user.id}) }}" method="post" {{ form_enctype(form) }} class="register">
{{ form_errors(form) }}
{{ form_row(form.firstname) }}
{{ form_row(form.lastname) }}
{{ form_row(form.username) }}
<p>
<input type="submit" value="Submit">
</p>
</form>
The problem with the EDIT code however is that it displays me all of the fields mentioned in RegisterType class.Is it possible to display only some fields. If yes how can this be achieved. Any help will be appreciated
It doesn't make sense to use the registration form type to edit a user, because registration happens once per user. Instead you could create another form type with only those fields you need when editing a user. One can extend the other to avoid duplication.
You could also:
Keep just one form type but add some fields conditionally — that is, only when the entity is new. You can get your entity in the form type as $options['data'] and check if its ID is not null or whatever.
Use form events.

Resources