8000 Added cookbook to show how to make a simple upload (ref to #2346 issue) · xabbuh/symfony-docs@8e2f8a2 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8e2f8a2

Browse files
saro0hxabbuh
authored andcommitted
Added cookbook to show how to make a simple upload (ref to symfony#2346 issue)
-- --
1 parent 7a6e3d1 commit 8e2f8a2

File tree

1 file changed

+270
-0
lines changed

1 file changed

+270
-0
lines changed

cookbook/controller/upload_file.rst

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,270 @@
1+
.. index::
2+
single: Controller; Upload; File
3+
4+
How to upload files
5+
===================
6+
7+
Let's begin with the creation of an entity Product having a document property to
8+
which will contain the description of that product. We'll also indicate the
9+
validation needed for each properties of the entity.
10+
11+
So let's say we have a product with a name, a price and a document which must be
12+
a PDF file::
13+
14+
// src/Vendor/ShopBundle/Entity/Product.php
15+
namespace Vendor\ShopBundle\Entity;
16+
17+
use Symfony\Component\Validator\Constraints as Assert;
18+
19+
class Product
20+
{
21+
/**
22+
* @Assert\NotBlank(message="You must indicate a name to your product.")
23+
*/
24+
private $name;
25+
26+
/**
27+
* @Assert\NotBlank(message="You must indicate a price to your product.")
28+
* @Assert\Type(type="float", message="Amount must be a valid number.")
29+
*/
30+
private $price;
31+
32+
/**
33+
* @Assert\NotBlank(message="You must upload a description with a PDF file.")
34+
* @Assert\File(mimeTypes={ "application/pdf" })
35+
*/
36+
private $document;
37+
38+
public function getName()
39+
{
40+
return $this->name;
41+
}
42+
43+
public function setName($name)
44+
{
45+
$this->name = $name;
46+
47+
return $this;
48+
}
49+
50+
public function getPrice()
51+
{
52+
return $this->price;
53+
}
54+
55+
public function setPrice($price)
56+
{
57+
$this->price = $price;
58+
59+
return $this;
60+
}
61+
62+
public function getDocument()
63+
{
64+
return $this->document;
65+
}
66+
67+
public function setDocument($document)
68+
{
69+
$this->document = $document;
70+
71+
return $this;
72+
}
73+
}
74+
75+
We also made sure that the user will have to indicate information to each fields.
76+
To know more about validation, take a look at the :doc:`validation book </book/validation>`
77+
chapter.
78+
79+
You have now to create the ``ProductType`` with those three fields as following::
80+
81+
// src/Vendor/ShopBundle/Form/ProductType.php
82+
namespace Vendor\ShopBundle\Form;
83+
84+
use Symfony\Component\Form\AbstractType;
85+
use Symfony\Component\Form\FormBuilderInterface;
86+
87+
class ProductType extends AbstractType
88+
{
89+
public function buildForm(FormBuilderInterface $builder, array $options)
90+
{
91+
$builder
92+
->add('name', 'text', array('label' => 'Name:'))
93+
->add('price', 'money', array('label' => 'Price:'))
94+
->add('document', 'file', array('label' => 'Upload description (PDF file):'))
95+
->add('submit', 'submit', array('label' => 'Create!'))
96+
;
97+
}
98+
99+
public function getName()
100+
{
101+
return 'product';
102+
}
103+
}
104+
105+
Now, make it as a service so it can be used anywhere easily::
106+
107+
.. configuration-block::
108+
109+
.. code-block:: yaml
110+
111+
# src/Vendor/ShopBundle/Resources/config/services.yml
112+
services:
113+
vendor.form.product_type:
114+
class: Vendor\ShopBundle\Form\ProductType
115+
tags:
116+
- { name: form.type }
117+
118+
# Import the services.yml file of your bundle in your config.yml
119+
imports:
120+
- { resource: "@VendorShopBundle/Resources/config/services.yml" }
121+
122+
.. code-block:: xml
123+
124+
<!-- src/Vendor/ShopBundle/Resources/config/services.xml -->
125+
<services>
126+
<service id="vendor.form.product_type" class="Vendor\ShopBundle\Form\ProductType">
127+
<tag name="form.type" alias="product" />
128+
</service>
129+
</services>
130+
131+
.. code-block:: php
132+
133+
// src/Vendor/ShopBundle/DependencyInjection/VendorShopExtension.php
134+
use Symfony\Component\DependencyInjection\Definition;
135+
136+
//…
137+
138+
$definition = new Definition('Vendor\ShopBundle\Form\ProductType');
139+
$container->setDefinition('vendor.form.product_type', $definition);
140+
$definition->addTag('form.type');
141+
142+
If you never dealt with services before, take some time to read the
143+
:doc:`book Service </book/service_container>` chapter.
144+
145+
146+
We must display the form to our users. To do that, create the controller as
147+
following::
148+
149+
// src/Vendor/ShopBundle/Controller/ProductController.php
150+
namespace Vendor\ShopBundle\Controller;
151+
152+
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
153+
use Symfony\Component\HttpFoundation\Request;
154+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
155+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
156+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
157+
use Vendor\ShopBundle\Entity\Product;
158+
159+
class ProductController extends Controller
160+
{
161+
/**
162+
* @Route("/product/new", name="vendor_product_new")
163+
* @Template()
164+
* @Method({"GET", "POST"})
165+
*/
166+
public function newAction(Request $request)
167+
{
168+
$product = new Product();
169+
$form = $this->createForm('product', $product);
170+
$form->handleRequest($request);
171+
172+
return array('form' => $form->createView());
173+
}
174+
}
175+
176+
Create the corresponding template as following::
177+
178+
.. code-block:: html+jinja
179+
180+
{# src/Vendor/ShopBundle/Resources/views/Product/new.html.twig #}
181+
{% form_theme form _self %}
182+
183+
<h1>Creation of a new Product</h1>
184+
185+
<form action="{{ path('vendor_product_new') }}" method="POST" {{ form_enctype(form) }}>
186+
{{ form_widget(form) }}
187+
</form>
188+
189+
{% block form_row %}
190+
{% spaceless %}
191+
<fieldset>
192+
<legend>{{ form_label(form) }}</legend>
193+
{{ form_errors(form) }}
194+
195+
{{ form_widget(form) }}
196+
</fieldset>
197+
{% endspaceless %}
198+
{% endblock form_row %}
199+
200+
We added some sugar by adapting our form with a form theme (take a look at the
201+
:doc:`form themes </cookbook/form/form_customization#what-are-form-themes>` to
202+
know more about the subject).
203+
204+
We now have our form displayed. Let's complete our action to deal with the
205+
upload of our document::
206+
207+
// src/Vendor/ShopBundle/Controller/ProductController.php
208+
209+
class ProductController extends Controller
210+
{
211+
/**
212+
* @Route("/product/new", name="vendor_product_new")
213+
* @Template()
214+
* @Method({"GET", "POST"})
215+
*/
216+
public function newAction(Request $request)
217+
{
218+
//…
219+
220+
if ($form->isValid()) {
221+
222+
$file = $product->getDocument()
223+
224+
// Compute the name of the file.
225+
$name = md5(uniqid()).'.'.$file->guessExtension();
226+
227+
$file = $file->move(__DIR__.'/../../../../web/uploads', $name);
228+
$product->setDocument($filename);
229+
230+
// Perform some persistance
231+
232+
$this->getSession()->getFlashBag()->add('notice', 'The upload has been well uploaded.');
233+
234+
return $this->redirect($this->generateUrl('vendor_product_new'));
235+
}
236+
237+
return array('form' => $form->createView());
238+
}
239+
}
240+
241+
The :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::guessExtension()`
242+
returns the extension of the file the user just uploaded.
243+
244+
Note the :method:`Symfony\\Component\\HttpFoundation\\File\\UploadedFile::move`
245+
method allowing movement of the file
246+
247+
We must display the flash message in our template::
248+
249+
.. code-block:: html+jinja
250+
251+
{# src/Vendor/ShopBundle/Resources/views/Product/new.html.twig #}
252+
253+
{# … #}
254+
{% for flashes in app.session.flashbag.all %}
255+
{% for flashMessage in flashes %}
256+
<ul>
257+
<li>{{ flashMessage }}</li>
258+
</ul>
259+
{% endfor %}
260+
{% endfor %}
261+
{# … #}
262+
263+
The file is now uploaded in the folder ``web/upload`` of your project.
264+
265+
.. note::
266+
267+
For the sake of testability and maintainability, it is recommended to put the
268+
logic inherent to the upload in a dedicated service. You could even make the
269+
path to the upload folder as a configuration parameter injected to your service.
270+
That way, you make the upload feature more flexible.

0 commit comments

Comments
 (0)
0