Здравствуйте.
Начинаю разбираться с framework-ом Symfony.
Проект создан на Symfony 3.0.4.
Создал две сущности:
Листинг Product.php:
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Product
*
* @ORM\Table(name="product")
* @ORM\Entity(repositoryClass="AppBundle\Repository\ProductRepository")
*/
class Product
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* @ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=30)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="description", type="text")
*/
private $description;
/**
* @var string
*
* @ORM\Column(name="price", type="decimal", precision=10, scale=0)
*/
private $price;
/**
* @var \DateTime
*
* @ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* @ORM\OneToMany(targetEntity="Image", mappedBy="product")
*/
private $images;
function __construct()
{
$this->images = new ArrayCollection();
}
// getters & setters ...
Листинг Image.php:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Image
*
* @ORM\Table(name="image")
* @ORM\Entity(repositoryClass="AppBundle\Repository\ImageRepository")
*/
class Image
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="image", type="string", length=255)
*/
private $image;
/**
* @ORM\ManyToOne(targetEntity="Product", inversedBy="images")
* @ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
// getters & setters ...
В поле image (потом переименую в более подходящее имя) таблицы Image буду хранить имя картинки, чтобы в будущем по добавляя путь до папки с загруженными изображениями, формировался полный путь.
Схема БД:
На данный момент у меня создана форма ProductType.php:
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('description', TextareaType::class)
->add('price', NumberType::class)
->add('category', EntityType::class, array(
'class' => 'AppBundle\Entity\Category',
'choice_label' => 'category'
))
->getForm();
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'register_class' => 'AppBundle:Entity:Product'
));
}
public function getName()
{
return 'app_bundle_product_type';
}
}
Теперь в неё нужно добавить поле с загрузкой файлов.
Я искал примеры в документации, но там был пример с одним файлом и ещё один, но там тоже не объяснялось как сделать множественную загрузку и другие детали.
У меня есть предположение, что можно добавить к форме Product ещё поле:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('description', TextareaType::class)
->add('price', NumberType::class)
->add('category', EntityType::class, array(
'class' => 'AppBundle\Entity\Category',
'choice_label' => 'category'
))
->add('images', ColeectionType:class) // ДОБАВЛЕННОЕ ПОЛЕ
->getForm();
}
но это же не сработает.
Вообщем нужна ваша помощь, уже не второй день ищу ответ на мой вопрос.
UPDATED
Нашёл на
stackoverflow то, что мне нужно.
Изменения:
Image.php - добавил поля name, path и file (name, path хранятся в БД, file - нет):
/**
* Image
*
* @ORM\Table(name="image")
* @ORM\Entity(repositoryClass="AppBundle\Repository\ImageRepository")
*/
class Image
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=50)
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="path", type="string", length=255)
*/
private $path;
/**
* @ORM\ManyToOne(targetEntity="Product", inversedBy="images")
* @ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
/**
* @Assert\File(maxSize="6000000")
*/
private $file;
// геттеры и сеттеры
Product.php - в аннотации добавил каскадную вставку в таблицу Image, и добавил метод addImage(Image):
/**
* @ORM\OneToMany(targetEntity="Image", mappedBy="product", cascade={"persist"})
*/
private $images;
public function addImage(Image $image) {
$this->images[] = $image;
$image->setProduct($this);
return $this;
}
Далее создал форму для Image - ImageType.php:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('file', FileType::class)
->getForm();
}
Добавил поле images в форму ProductType.php:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('description', TextareaType::class)
->add('price', NumberType::class)
->add('category', EntityType::class, array(
'class' => 'AppBundle\Entity\Category',
'choice_label' => 'category'
))
->add('images', CollectionType::class, array(
'entry_type' => ImageType::class,
'allow_add' => true,
'by_reference' => false,
'allow_delete' => true,
'prototype' => true
))
->getForm();
}
тип формы images - колеекция состоящая из моего ImageType.
Код Action-а в контроллере:
public function addProductAction(Request $request)
{
$product = new Product();
$image = new Image();
$product->addImage($image);
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isValid()) {
$product->setDate(new \DateTime());
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
return $this->redirectToRoute('admin_products');
}
return $this->render('admin/add_product.html.twig', array(
'form' => $form->createView()
));
}
Это всё.
Теперь к проблемам.
При отправке заполненной формы выдает ошибку, говорящая о том, что поле name таблицы image не может быть null:
An exception occurred while executing 'INSERT INTO image (name, path, product_id) VALUES (?, ?, ?)' with params [null, "initial", 12]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'name' cannot be null
Что странно т.к. я ввел name при заполнении формы.
Ещё я не знаю как и что мне делать когда в конроллер придёт ответ с заполненной формой. Перед тем как записать всё в БД нужно:
- Сохранить файл
- Записать в path путь
- Записать в name имя файла
(это не считая всяких проверок на существование файла с таким же именем и прочее). - Вот это как сделать не знаю.
Вторая проблема с такой реализацией в том, что я не могу выбрать сразу несколько файлов. Мне придется в addProductAction инициализировать несколько новых файлов:
$image1 = new Image();
$product->addImage($image1);
$image2 = new Image();
$product->addImage($image2);
Но тогда пользователь сможет загрузить только заданное кол-во изображений.
Скорее всего эту проблему можно решить динамическим добавлением форм при помощи javascript, но я хотел бы иметь одну форму, а у пользователя была возможность выделить несколько файлов за раз.
Уверен что каждый кто работал с symfony знает как это делается. Помогите пожалуйста.