I am trying to upload a file with symfony. But I have an error when I send data formulaire to controller. Variable file is null and function move file don't working.
code entity (par uplaod):
<?php
namespace AppBundle\Entity;
use AppBundle\Model\CompanyInterface;
use AppBundle\Model\RecruiterInterface;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Company.
*
* #ORM\Table(name="company")
* #ORM\Entity(repositoryClass="AppBundle\Repository\CompanyRepository")
*/
class Company implements CompanyInterface
{
/**
* #var string
*
* #ORM\Column(name="id", type="guid")
* #ORM\Id
* #ORM\GeneratedValue(strategy="UUID")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, unique=true)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #var array
*
* #ORM\Column(name="activities", type="array")
*/
private $activities;
/**
* #var RecruiterInterface
*
* #ORM\OneToOne(targetEntity="AppBundle\Entity\Recruiter", cascade={"persist"})
*/
private $recruiter;
/**
* #var string
*
* #ORM\Column(name="path", type="string", length=255,nullable=true)
*/
private $logo;
/**
* #Assert\File(maxSize="6000000")
*/
private $file;
/**
* {#inheritdoc}
*/
public static function create(string $name, RecruiterInterface $recruiter, string $description = '', array $activities = []): CompanyInterface
{
return new self($name, $recruiter, $description, $activities);
}
/**
* {#inheritdoc}
*/
public function update(string $name, string $description, array $activities): CompanyInterface
{
$this->name = $name;
$this->description = $description;
$this->activities = $activities;
return $this;
}
/**
* {#inheritdoc}
*/
public function updateName(string $name): CompanyInterface
{
$this->name = $name;
return $this;
}
/**
* {#inheritdoc}
*/
public function getId(): string
{
return $this->id;
}
/**
* {#inheritdoc}
*/
public function getName(): ?string
{
return $this->name;
}
/**
* function setName.
*
* #param string $name
*
* #return string
*/
public function setName(string $name)
{
$this->name = $name;
return $this;
}
/**
* function setDescription.
*
* #param string $description
*
* #return string
*/
public function setDescription(string $description)
{
$this->description = $description;
return $this;
}
/**
* function setActivities.
*
* #param string $activities
*
* #return string
*/
public function setActivities(string $activities)
{
$this->activities = $activities;
return $this;
}
/**
* {#inheritdoc}
*/
public function getDescription(): ?string
{
return $this->description;
}
/**
* {#inheritdoc}
*/
public function getActivities()
{
return $this->activities;
}
/**
* {#inheritdoc}
*/
public function getRecruiter(): ?RecruiterInterface
{
return $this->recruiter;
}
/**
* #return string
*/
public function __toString()
{
return $this->name;
}
/**
* {#inheritdoc}
*/
public function updateLogo(string $logo): CompanyInterface
{
$this->logo = $logo;
return $this;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
/**
* Set logo.
*
* #param string $logo
*
* #return Company
*/
public function setLogo($logo)
{
$this->logo = $logo;
return $this;
}
/**
* Get logo.
*
* #return string
*/
public function getLogo()
{
return $this->logo;
}
/**
* function upload.
*/
public function upload()
{
// the file property can be empty if the field is not required
if (null === $this->getFile()) {
return;
}
$this->file->move($this->getUploadRootDir(),$this->path);
// set the path property to the filename where you've saved the file
$this->logo = $this->getFile()->getClientOriginalName();
// clean up the file property as you won't need it anymore
//$this->file = null;
}
/**
* function getUploadRootDir.
*/
protected function getUploadRootDir()
{
return __DIR__.'/../../../web/uploads';
}
}
code formType(content variable file):
<?php
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
/**
* Class CompanyType.
*/
class CompanyType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('description')
->add('activities', TextType::class)
->add('file', FileType::class)
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Company',
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return '';
}
}
code other FormTyp(I use form imbrique):
namespace AppBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use AppBundle\Entity\Recruiter;
/**
* Class RecruiterType.
*/
class RecruiterType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$builder->remove('username');
$builder->remove('plainPassword');
$builder
->add('email')
->add('lastname')
->add('firstname')
->add('phone')
->add('civility', ChoiceType::class, array(
'choices' => ['Mr' => 'Mr', 'Mrs' => 'Mrs'], 'expanded' => true, 'multiple' => false,
))
->add('company', CompanyType::class)
;
}
/**
* function get Parent Form.
*
* #return string
*/
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
}
/**
* name for this form.
*
* #return string
*/
public function getBlockPrefix()
{
return '';
}
/**
* name for this form.
*
* #return string
*/
public function getName()
{
return $this->getBlockPrefix();
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Recruiter::class,
'allow_extra_fields' => true,
'csrf_protection' => false,
));
}
}
function post (persist object):
public function post(array $parameters, bool $submitted = true, array $options = [])
{
/** #var Recruiter $recruiter */
$recruiter = $this->formHandler->handle(
$this->userManager->createRecruiter(),
$parameters,
Request::METHOD_POST,
$submitted,
$options
);
if (false === $submitted) {
return $recruiter;
}
$recruiter->submit($parameters);
if ($recruiter->isSubmitted() && $recruiter->isValid()) {
$recruiter->getData()->setCompany($recruiter->getData()->getCompany());
$recruiter->getData()->getCompany()->upload();
$this->em->persist($recruiter->getData());
$this->em->flush();
$event = new FormEvent($recruiter, $this->request);
$this->eventDispatcher->dispatch(FOSUserEvents::REGISTRATION_SUCCESS, $event);
return $recruiter->getData();
}
return $recruiter;
}
code controller :
<?php
namespace AppBundle\Controller\Recruiter;
use FOS\UserBundle\Controller\RegistrationController as BaseController;
use FOS\UserBundle\Event\GetResponseUserEvent;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use FOS\UserBundle\FOSUserEvents;
use FOS\UserBundle\Event\FormEvent;
use AppBundle\Handler\RecruiterHandler;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use AppBundle\Entity\Recruiter;
use Symfony\Component\HttpFoundation\RedirectResponse;
use AppBundle\Util\UserManagerr;
/**
* Class RegistrationRecruiterController.
*/
class RegistrationRecruiterController extends BaseController
{
/**
* #Template()
* #Route("/register", name="register_recruiter")
*
* #param Request $request
*
* #return array|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function registerAction(Request $request)
{
try {
$user = new UserManagerr();
$dispatcher = $this->get('event_dispatcher');
$user = $user->createRecruiter();
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
$recruiter = $this->getHandler()->post($request->request->all(), $request->isMethod(Request::METHOD_POST));
if ($recruiter instanceof Recruiter) {
$url = $this->generateUrl('fos_user_registration_check_email');
$response = new RedirectResponse($url);
return $response;
}
} catch (InvalidFormException $e) {
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::REGISTRATION_FAILURE, $event);
return [
'form' => $e->getForm()->createView(),
'edit' => false,
'event' => $event->getResponse(),
];
}
return ['form' => $recruiter->createView()];
}
/**
* #return RecruiterHandler
*/
public function getHandler()
{
return $this->get(RecruiterHandler::class);
}
}
I use form imbriqu (form in other form) but when I send form I have all variable from object but variable file has null and function move doesn't work.
How to resolve this error please?
Thanks in advance for any help.
Change this
public function post(array $parameters, bool $submitted = true, array $options = [])
For
public function post(Request $request, bool $submitted = true, array $options = [])
[...]
$recruiter->submit($request);
[...]
Your issue is probably in the for tag, which you should declare it like:
{{ form_start(form, {'method': 'post', 'action': '', 'attr': {'class': '', 'novalidate': 'novalidate' }}) }}
Related
I created a demo1 entity with a one-to-many relationship Image entity and using vich bundle package for uploading
Demo1 enetity
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use App\Controller\UploadImageAction;
/**
* #ORM\Entity(repositoryClass="App\Repository\Demo1Repository")
* #ApiResource(
* collectionOperations={
* "get",
* "post" ={
*
* "controller"=UploadImageAction::class,
* "defaults"={"_api_receive"=false},
* "denormalization_context"={"groups"={"image"}}
* }
* }
* )
*/
class Demo1
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Image", mappedBy="myfiles")
* #ORM\JoinColumn(nullable=false);
*/
private $files;
public function __construct()
{
$this->files = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
/**
* #return Collection|Image[]
*/
public function getFiles(): Collection
{
return $this->files;
}
public function addFile(Image $file): self
{
if (!$this->files->contains($file)) {
$this->files[] = $file;
$file->setMyfiles($this);
}
return $this;
}
public function removeFile(Image $file): self
{
if ($this->files->contains($file)) {
$this->files->removeElement($file);
// set the owning side to null (unless already changed)
if ($file->getMyfiles() === $this) {
$file->setMyfiles(null);
}
}
return $this;
}
}
Image entity
<?php
namespace App\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\Serializer\Annotation\Groups;
use App\Controller\UploadImageAction;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Api\Data\TagInterface;
use App\Api\File\ImageInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="data_files_images")
* #ORM\Entity(repositoryClass="App\Repository\ImageRepository")
* #Vich\Uploadable()
*/
class Image extends AbstractEntity implements ImageInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"get","patch","post"})
*/
private $id;
/**
* #ORM\Column(name="mimetype", type="string", length=20, nullable=true)
*/
private $mimeType;
/**
* #Vich\UploadableField(mapping="images", fileNameProperty="url")
* #Assert\NotNull()
* #Groups({"image"})
*/
private $files;
/**
* #ORM\Column(name="url", type="string", length=255, nullable=true)
* #Groups({"get","patch","post"})
*/
private $url;
/**
* #ORM\Column(name="width", type="integer", nullable=true)
*/
private $width;
/**
* #ORM\Column(name="length", type="integer", nullable=true)
*/
private $length;
/**
* #ORM\Column(name="filesize", type="integer", nullable=true)
*/
private $filesize;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Tag", inversedBy="images", cascade={"persist"})
* #ORM\JoinTable(
* name="rel_tags_images",
* joinColumns={ #ORM\JoinColumn(name="image_id", referencedColumnName="id", onDelete="CASCADE") },
* inverseJoinColumns={ #ORM\JoinColumn(name="tag_id", referencedColumnName="id", onDelete="CASCADE") }
* )
*/
private $tags;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\SingleProduct", inversedBy="images")
* #ORM\JoinColumn(name="product_id", nullable=true, onDelete="CASCADE")
*/
private $singleProduct;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Demo1", inversedBy="files")
* ORM\#ORM\JoinColumn(nullable=false)
*/
public $myfiles;
/**
* Image constructor.
*/
public function __construct()
{
$this->tags = new ArrayCollection();
}
/**
* #return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* #return string|null
*/
public function getMimeType(): ?string
{
return $this->mimeType;
}
/**
* #param string $mimeType
*/
public function setMimeType(string $mimeType): void
{
$this->mimeType = $mimeType;
}
/**
* #return string|null
*/
public function getUrl(): ?string
{
return $this->url;
}
/**
* #param string $url
*/
public function setUrl(string $url): void
{
$this->url = $url;
}
/**
* #return int|null
*/
public function getWidth(): ?int
{
return $this->width;
}
/**
* #param int $width
*/
public function setWidth(int $width): void
{
$this->width = $width;
}
/**
* #return int|null
*/
public function getLength(): ?int
{
return $this->length;
}
/**
* #param int $length
*/
public function setLength(int $length): void
{
$this->length = $length;
}
/**
* #return int|null
*/
public function getFilesize(): ?int
{
return $this->filesize;
}
/**
* #param int $filesize
*/
public function setFilesize(int $filesize): void
{
$this->filesize = $filesize;
}
/**
* #return Collection|Tag[]
*/
public function getTags(): Collection
{
return $this->tags;
}
/**
* #param \App\Entity\Tag $tag
*/
public function addTag(Tag $tag): void
{
if (!$this->tags->contains($tag)) {
$this->tags[] = $tag;
}
}
/**
* #param \App\Entity\Tag $tag
*/
public function removeTag(Tag $tag): void
{
if ($this->tags->contains($tag)) {
$this->tags->removeElement($tag);
}
}
/**
* #return string
*/
public function toString(): string
{
return (string)$this->getUrl();
}
/**
* #return array
*/
public function toArray(): array
{
return [
'mimetype' => $this->getMimeType(),
'url' => $this->getUrl(),
'width' => $this->getWidth(),
'length' => $this->getLength(),
'filesize' => $this->getFilesize(),
'tags' => array_map(
function (TagInterface $tag) {
return $tag->toString();
},
$this->getTags()->toArray()
),
];
}
/**
* #return \App\Entity\SingleProduct|null
*/
public function getSingleProduct(): ?SingleProduct
{
return $this->singleProduct;
}
/**
* #param \App\Entity\SingleProduct|null $singleProduct
*/
public function setSingleProduct(?SingleProduct $singleProduct): void
{
$this->singleProduct = $singleProduct;
}
/**
* #return mixed
*/
public function getFiles()
{
return $this->files;
}
/**
* #param mixed $files
*/
public function setFiles($files): void
{
$this->files = $files;
}
public function getMyfiles(): ?Demo1
{
return $this->myfiles;
}
public function setMyfiles(?Demo1 $myfiles): self
{
$this->myfiles = $myfiles;
return $this;
}
}
ImageType (form)
<?php
namespace App\Form;
use App\Entity\Demo1;
use App\Entity\Image;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ImageType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('files', FileType::class, [
'multiple'=>true,
'attr' => [
'accept' => 'image/*',
]
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Demo1::class,
'csrf_protection' => false
]);
}
public function getBlockPrefix()
{
return '';
}
}
Upload action
<?php
namespace App\Controller;
use ApiPlatform\Core\Validator\Exception\ValidationException;
use ApiPlatform\Core\Validator\ValidatorInterface;
use App\Entity\Demo1;
use App\Entity\Image;
use App\Form\ImageType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\Request;
class UploadImageAction
{
/**
* #var FormFactoryInterface
*/
private $formFactory;
/**
* #var EntityManagerInterface
*/
private $entityManager;
/**
* #var ValidatorInterface
*/
private $validator;
public function __construct(
FormFactoryInterface $formFactory,
EntityManagerInterface $entityManager,
ValidatorInterface $validator
)
{
$this->formFactory = $formFactory;
$this->entityManager = $entityManager;
$this->validator = $validator;
}
public function __invoke(Request $request)
{
// Create a new Image instance
$image = new Demo1();
// Validate the form
$form = $this->formFactory->create(ImageType::class, $image);
$form->handleRequest($request);
// dump($request);
if ($form->isSubmitted() && $form->isValid()){
// Persist the new Image entity
$this->entityManager->persist($image);
$this->entityManager->flush();
// $image->setFiles(null);
return $image;
}
// Uploading done for us in background by VichUploader
// Throw an validation exception, that means something went wrong during
// form validation
throw new ValidationException(
$this->validator->validate($image)
);
}
}
if any alternative solution to upload multiple files with platform API suggests that also
the current code not uploading files and not updating image table as well
Please find below an example for uploading multiple attachments to a message.
/App/Entity/Attachment
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use App\Controller\AttachmentMessageAction;
/**
* #ApiResource(
* attributes={"pagination_enabled"=false},
* itemOperations={
* "get"={
* "access_control"="is_granted('ROLE_USER')",
* "normalization_context"={
* "groups"={"get"}
* }
* }
* },
* collectionOperations={
* "get",
* "post"={
* "method"="POST",
* "path"="/attachments",
* "controller"=AttachmentMessageAction::class,
* "defaults"={"_api_receive"=false}
* }
* }
* )
* #ORM\Entity(repositoryClass="App\Repository\AttachmentRepository")
* #Vich\Uploadable()
*/
class Attachment
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"get"})
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Message", inversedBy="attachments")
* #ORM\JoinColumn(nullable=true)
* #Groups({"post", "get"})
*/
private $message;
/**
* #Assert\File(
* maxSize = "2048k",
* maxSizeMessage = "File exceeds allowed size",
* mimeTypes = {"image/png","image/jpeg", "application/pdf", "application/x-pdf"},
* mimeTypesMessage = "Please upload a valid file"
* )
* #Assert\NotNull()
* #Vich\UploadableField(mapping="message_attachment", fileNameProperty="filename")
* #Groups({"post"})
* #var File
*/
private $file;
/**
* #ORM\Column(type="string", length=180)
* #Groups({"post","get-owner"})
* #var string
*/
private $filename;
/**
* #ORM\Column(type="datetime")
* #var \DateTime
*/
private $updatedAt;
/**
* Constructor
*
* #param Message $message
*/
public function __construct(Message $message = null)
{
$this->message = $message;
}
public function getId()
{
return $this->id;
}
public function getMessage(): ?Message
{
return $this->message;
}
public function setMessage(?Message $message): self
{
$this->message = $message;
return $this;
}
/**
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $attachment
*/
public function setFile(?File $file = null): void
{
$this->file = $file;
if (null !== $file) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTimeImmutable();
}
}
public function getFile(): ?File
{
return $this->file;
}
public function getFilename(): ?string
{
return $this->filename;
}
public function setFilename(?string $filename): void
{
$this->filename = $filename;
}
public function getUpdatedAt(): ?\DateTimeInterface
{
return $this->updatedAt;
}
public function setUpdatedAt(\DateTimeInterface $updatedAt): self
{
$this->updatedAt = $updatedAt;
return $this;
}
public function __toString()
{
return $this->id . ':' . $this->filename;
}
}
And App/Controller/AttachmentMessageAction
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use ApiPlatform\Core\Validator\ValidatorInterface;
use ApiPlatform\Core\Validator\Exception\ValidationException;
use App\Entity\Attachment;
use App\Entity\Message;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\HttpFoundation\Request;
class AttachmentMessageAction extends AbstractController
{
/**
* #var ValidatorInterface
*/
private $validator;
/**
* #var EntityManagerInterface
*/
private $entityManager;
public function __construct(
ValidatorInterface $validator,
EntityManagerInterface $entityManager
)
{
$this->validator = $validator;
$this->entityManager = $entityManager;
}
public function __invoke(Request $request)
{
$files = $request->files->get('file');
$attachments = [];
foreach ($files as $file) {
$attachment = new Attachment();
$attachment->setFile($file);
$this->validator->validate($attachment);
$this->entityManager->persist($attachment);
$attachment->setFile(null);
$attachments[] = $attachment;
}
$this->entityManager->flush();
return $attachments;
}
}
EDIT: Relevant excerpts of App/Entity/Message as requested:
/**
* #ORM\OneToMany(targetEntity="App\Entity\Attachment", mappedBy="message",cascade={"persist"}, orphanRemoval=true)
* #Groups({"post", "get"})
*/
private $attachments;
....
public function __construct()
{
$this->attachments = new ArrayCollection();
$this->children = new ArrayCollection();
$this->isDeletedBySender = false;
$this->isDeletedByReceiver = false;
$this->sentAt = new \DateTimeImmutable();
}
....
/**
* #return Collection|Attachment[]
*/
public function getAttachments(): Collection
{
return $this->attachments;
}
public function addAttachment(Attachment $attachment): self
{
if (!$this->attachments->contains($attachment)) {
$this->attachments[] = $attachment;
$attachment->setMessage($this);
}
return $this;
}
public function removeAttachment(Attachment $attachment): self
{
if ($this->attachments->contains($attachment)) {
$this->attachments->removeElement($attachment);
// set the owning side to null (unless already changed)
if ($attachment->getMessage() === $this) {
$attachment->setMessage(null);
}
}
return $this;
}
I don't find solutions for several days to resolve my issue. I want to upload multiple images when I create a Post. My Post entity has a OneToMany relation with Image Entity. I use an embedded form in my PostType. It is a CollectionType of ImageType::class. To manage my upload functionality, I wrote an event listener called ImageUploadListener which injects my custom service called FileUploader.
This listener call a uploadFile function when a preUpdate and a prePersist event is handled. This function call my upload function from my FileUploader to move the image/file... to a target directory and to return a filename.
After that, I try to instantiate an Image Entity and to set the appropriate datas. But it doesn't work, only a file seems to be stored properly but with an additional entry unwanted in my db. (In my Image Table, for one image uploaded, I've got an entry with id 1 for example, a post_id set to NULL and file field set to /tmp/random number).
Please, could you help me ?
Post Entity
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Post
*
* #ORM\Table(name="post")
* #ORM\Entity(repositoryClass="UserBundle\Repository\PostRepository")
*/
class Post
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="Title", type="string", length=255)
*/
private $title;
/**
* #var string
*
* #ORM\Column(name="Content", type="text")
*/
private $content;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="posts")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="Image", mappedBy="post", cascade={"persist", "remove"})
*/
private $images;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
*
* #return Post
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set content
*
* #param string $content
*
* #return Post
*/
public function setContent($content)
{
$this->content = $content;
return $this;
}
/**
* Get content
*
* #return string
*/
public function getContent()
{
return $this->content;
}
/**
* Set post
*
* #param \
*
* #return Post
*/
public function setUser($user)
{
$this->user = $user;
return $this;
}
/**
* Get post
*
* #return \?
*/
public function getUser()
{
return $this->user;
}
/**
* Constructor
*/
public function __construct()
{
$this->images = new ArrayCollection();
}
/**
* Add image
*
* #param \UserBundle\Entity\Image $image
*
* #return Post
*/
public function addImage(\UserBundle\Entity\Image $image)
{
$this->images[] = $image;
return $this;
}
/**
* Remove image
*
* #param \UserBundle\Entity\Image $image
*/
public function removeImage(\UserBundle\Entity\Image $image)
{
$this->images->removeElement($image);
}
/**
* Get images
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getImages()
{
return $this->images;
}
}
Image Entity
namespace UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="UserBundle\Entity\ImageRepository")
*/
class Image
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", nullable=true)
*/
private $file;
public function setFile($file)
{
$this->file = $file;
return $this;
}
public function getFile()
{
return $this->file;
}
/**
* #ORM\ManyToOne(targetEntity="Post", inversedBy="images")
* #ORM\JoinColumn(name="post_id", referencedColumnName="id")
*/
private $post;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set post
*
* #param \UserBundle\Entity\Post $post
*
* #return Image
*/
public function setPost(\UserBundle\Entity\Post $post = null)
{
$this->post = $post;
return $this;
}
/**
* Get post
*
* #return \UserBundle\Entity\Post
*/
public function getPost()
{
return $this->post;
}
}
PostType
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
//use Ivory\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use UserBundle\Entity\Post;
class PostType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('content')
->add('images', CollectionType::class, array (
'entry_type' => ImageType::class,
'entry_options' => array('label' => false),
'allow_add' => true,
'allow_delete' => true
))
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
//'data_class' => 'UserBundle\Entity\Post',
'data_class' => Post::class,
//'csrf_protection' => false
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'userbundle_post';
}
}
ImageType
namespace UserBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use UserBundle\Entity\Image;
class ImageType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, [
'required' => false,
'data_class' => null,
])
;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Image::class,
//'csrf_protection' => false
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'userbundle_image';
}
}
EDIT: ImageUploadListener
//src/UserBundle/EventListener/ImageUploadListener.php
namespace UserBundle\EventListener;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\File;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use UserBundle\Entity\User;
use UserBundle\Entity\Post;
use UserBundle\Entity\Image;
use UserBundle\Service\FileUploader;
class ImageUploadListener
{
private $uploader;
public function __construct(FileUploader $uploader)
{
$this->uploader = $uploader;
}
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
public function preUpdate(PreUpdateEventArgs $args)
{
$entity = $args->getEntity();
$this->uploadFile($entity);
}
private function uploadFile($entity)
{
if ($entity instanceof Post) {
$post = $entity;
$images = $post->getImages();
foreach ($images as $image) {
if ($image->getFile() instanceof UploadedFile) {
$imageName = $this->uploader->upload($image->getFile());
// to avoid persisting FileObject in DB
$post->removeImage($image);
$postImage = new Image();
$postImage->setFile($imageName);
$postImage->setPost($post);
$post->addImage($postImage);
}
}
}
return;
}
}
FileUploader
namespace UserBundle\Service;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class FileUploader
{
private $targetDir;
public function __construct($targetDir)
{
$this->targetDir = $targetDir;
}
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->getTargetDir(), $fileName);
return $fileName;
}
public function getTargetDir()
{
return $this->targetDir;
}
}
I have e little problem. I followed this tutorial to create a register formular but it doesn't persist the entity. I don't understand why. It doesn't create any mistake, it just... doesn't flush.
Here is the tutorial:
http://symfony.com/fr/doc/2.5/cookbook/doctrine/registration_form.html
Here is the entity:
namespace theia\mainBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* UserMain
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="theia\mainBundle\Entity\UserMainRepository")
*/
class UserMain
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, unique=true)
* #Assert\NotBlank()
* #Assert\Email()
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255)
* #Assert\NotBlank()
*/
private $password;
/**
* #var array
*
* #ORM\Column(name="roles", type="array")
*/
private $roles;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set email
*
* #param string $email
* #return UserMain
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set password
*
* #param string $password
* #return UserMain
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set roles
*
* #param array $roles
* #return UserMain
*/
public function setRoles($roles)
{
$this->roles = $roles;
return $this;
}
/**
* Get roles
*
* #return array
*/
public function getRoles()
{
return $this->roles;
}
public function __construct()
{
$this->roles = [ 'ROLE_USER' ];
}
}
here is my "Security Controller":
namespace theia\mainBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\HttpFoundation\Request;
use theia\mainBundle\Form\Type\RegistrationType;
use theia\mainBundle\Form\Model\Registration;
class SecurityController extends Controller
{
public function loginAction(Request $request)
{
$session = $request->getSession();
if ($request->attributes->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
$error = $request->attributes->get(
SecurityContextInterface::AUTHENTIFICATION_ERROR
);
} elseif (null !== $session && $session->has(SecurityContextInterface::AUTHENTICATION_ERROR)) {
$error = $session->get(SecurityContextInterface::AUTHENTICATION_ERROR);
$session->remove(SecurityContextInterface::AUTHENTICATION_ERROR);
} else {
$error = null;
}
// last username entered by the user
$lastEmail = (null === $session) ? '' : $session->get(SecurityContextInterface::LAST_USERNAME);
return $this->render(
'theiamainBundle::security/login.html.twig',
array(
// last username entered by the user
'last_email' => $lastEmail,
'error' => $error,
)
);
}
public function loginCheckAction()
{
}
public function logoutAction()
{
}
public function registerAction()
{
$form = $this->createForm(new RegistrationType(), new Registration());
return $this->render('theiamainBundle:security:register.html.twig', array('form' => $form->createView()));
}
public function createAction()
{
$em = $this->getDoctrine()->getEntityManager();
$form = $this->createForm(new RegistrationType(), new Registration());
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$registration = $form->getData();
$em->persist($registration->getUser());
$em->flush();
return $this->redirect('theiamainBundle::security/login.html.twig');
}
return $this->render('theiamainBundle:security:register.html.twig', array('form' => $form->createView()));
}
}
Here is what is in the directory Form:
My UserMainType:
namespace theia\mainBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class UsermainType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('email', 'email');
$builder->add('plainPassword', 'repeated', array(
'first_name' => 'password',
'second_name' => 'confirm',
'type' => 'password',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'theia\mainBundle\Entity\UserMain'
));
}
public function getName()
{
return 'user';
}
}
Here is the RegistrationType
namespace theia\mainBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class RegistrationType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('user', new UsermainType());
//$builder->add('terms', 'checkbox', array('property_path' => 'termsAccepted'));
}
public function getName()
{
return 'registration';
}
}
Here is Registration
namespace theia\mainBundle\Form\Model;
use Symfony\Component\Validator\Constraints as Assert;
use theia\mainBundle\Entity\UserMain;
class Registration
{
/**
* #Assert\Type(type="theia\mainBundle\Entity\User")
*/
protected $user;
/*
* #Assert\NotBlank()
* #Assert\True()
*
protected $termsAccepted;
*/
public function setUser(User $user)
{
$this->user = $user;
}
public function getUser()
{
return $this->user;
}
/*
public function getTermsAccepted()
{
return $this->termsAccepted;
}
public function setTermsAccepted($termsAccepted)
{
$this->termsAccepted = (Boolean) $termsAccepted;
}
*/
}
Thanks you for your helps
I am not quite sure but I think the problem is that you haven't set
'data_class' => 'theia\mainBundle\Entity\Registration'
In the setDefaultOptions of your RegistrationType, therefore the RegistrationType will not be mapped to an entity, therefore the UserMain cannot be persisted since it is used in the subform of RegistrationType.
Hello I'm trying to make a file upload with a oneToMany relation between an Annonce and some images; So an Annonce can contain 0 or more images and an image is still attached to an Annonce. I think everything is ok with my entities but I get this error when I submit the form.
> Annonce.php
<?php
namespace MDB\AnnonceBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Annonce
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="MDB\AnnonceBundle\Entity\AnnonceRepository")
*/
class Annonce {
public function __construct() {
$this->date = new \Datetime();
$this->categories = new ArrayCollection();
$this->images= new ArrayCollection();
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=255)
*/
private $titre;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
/**
* #ORM\Column(name="date", type="date")
*/
private $date;
/**
* #var float
*
* #ORM\Column(name="prix", type="float")
*/
private $prix;
/**
* #ORM\ManyToOne(targetEntity="MDB\AdresseBundle\Entity\Ville", inversedBy="annonces")
*/
private $ville;
/**
* #ORM\ManyToMany(targetEntity="MDB\AnnonceBundle\Entity\Category", cascade={"persist"})
*/
private $categories;
/**
* #ORM\ManyToMany(targetEntity="MDB\UserBundle\Entity\User")
*
*/
private $wishlist;
/**
* #var boolean
*
* #ORM\Column(name="telAppear", type="boolean")
*/
private $telAppear;
/**
* #ORM\ManyToOne(targetEntity="MDB\UserBundle\Entity\User", inversedBy="annonces")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="MDB\PlatformBundle\Entity\Image", mappedBy="annonce")
*/
private $images;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set titre
*
* #param string $titre
* #return Annonce
*/
public function setTitre($titre) {
$this->titre = $titre;
return $this;
}
/**
* Get titre
*
* #return string
*/
public function getTitre() {
return $this->titre;
}
/**
* Set description
*
* #param string $description
* #return Annonce
*/
public function setDescription($description) {
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription() {
return $this->description;
}
/**
* Set prix
*
* #param float $prix
* #return Annonce
*/
public function setPrix($prix) {
$this->prix = $prix;
return $this;
}
/**
* Get prix
*
* #return float
*/
public function getPrix() {
return $this->prix;
}
public function addCategory(Category $category) {
// Ici, on utilise l'ArrayCollection vraiment comme un tableau
$this->categories[] = $category;
return $this;
}
public function removeCategory(Category $category) {
$this->categories->removeElement($category);
}
public function getCategories() {
return $this->categories;
}
public function getDate() {
return $this->date;
}
public function setDate($date) {
$this->date = $date;
}
public function getWishlist() {
return $this->wishlist;
}
public function setWishlist($wishlist) {
$this->wishlist = $wishlist;
}
public function getVille() {
return $this->ville;
}
public function setVille($ville) {
$this->ville = $ville;
}
public function getTelAppear() {
return $this->telAppear;
}
public function setTelAppear($telAppear) {
$this->telAppear = $telAppear;
}
public function getUser() {
return $this->user;
}
public function setUser($user) {
$this->user = $user;
}
public function addImage(Image $image) {
$this->images[] = $image;
$image->setUser($this);
return $this;
}
public function removeImage(Image $image) {
$this->images->removeElement($image);
}
public function getImages() {
return $this->images;
}
}
Image.php
<?php
namespace MDB\PlatformBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Image
*
* #ORM\Table()
* #ORM\Entity
*/
class Image {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*/
private $path;
/**
* #var string
*
* #ORM\Column(name="alt", type="string", length=255)
*/
private $alt;
/**
* #ORM\ManyToOne(targetEntity="MDB\AnnonceBundle\Entity\Annonce", inversedBy="images")
* #ORM\JoinColumn(nullable=true)
*/
private $annonce;
/**
* #Assert\File(maxSize="6000000")
*/
public $file;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set url
*
* #param string $url
* #return Image
*/
public function setUrl($url) {
$this->url = $url;
return $this;
}
/**
* Get url
*
* #return string
*/
public function getUrl() {
return $this->url;
}
/**
* Set alt
*
* #param string $alt
* #return Image
*/
public function setAlt($alt) {
$this->alt = $alt;
return $this;
}
/**
* Get alt
*
* #return string
*/
public function getAlt() {
return $this->alt;
}
public function getAnnonce() {
return $this->annonce;
}
public function setAnnonce($annonce) {
$this->annonce = $annonce;
}
public function getAbsolutePath() {
return null === $this->path ? null : $this->getUploadRootDir() . '/' . $this->path;
}
public function getWebPath() {
return null === $this->path ? null : $this->getUploadDir() . '/' . $this->path;
}
protected function getUploadRootDir() {
// le chemin absolu du répertoire où les documents uploadés doivent être sauvegardés
return __DIR__ . '/../../../../web/' . $this->getUploadDir();
}
protected function getUploadDir() {
// on se débarrasse de « __DIR__ » afin de ne pas avoir de problème lorsqu'on affiche
// le document/image dans la vue.
return 'uploads/documents';
}
public function getFile() {
return $this->file;
}
public function setFile($file) {
$this->file = $file;
}
}
AnnonceType.php
<?php
namespace MDB\AnnonceBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class AnnonceType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('titre')
->add('description')
->add('date')
->add('prix')
->add('telAppear', 'checkbox', array('required' => false))
->add('ville', new \MDB\AdresseBundle\Form\VilleType(), array('required' => true))
->add('categories')
->add('wishlist')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'MDB\AnnonceBundle\Entity\Annonce'
));
}
/**
* #return string
*/
public function getName() {
return 'mdb_annoncebundle_annonce';
}
}
AnnonceSellType.php (who herit of AnnonceType.php)
<?php
namespace MDB\AnnonceBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use \MDB\PlatformBundle\Form\ImageType;
class AnnonceSellType extends AbstractType {
private $arrayListCat;
public function __construct($arrayListCat) {
$this->arrayListCat = $arrayListCat;
}
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->remove('wishlist')
->remove('date')
->remove('categories')
->add('titre')
->add('description', 'textarea')
->add('prix')
->add('categories', 'choice', array(
'choices' => $this->arrayListCat,
'multiple' => true,
'mapped' => false,
))
->add('images', new ImageType())
;
}
/**
* #return string
*/
public function getName() {
return 'mdb_annoncebundle_annonce_sell';
}
public function getParent() {
return new AnnonceType();
}
}
ImageType.php
<?php
namespace MDB\PlatformBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ImageType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('file')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'data_class' => 'MDB\PlatformBundle\Entity\Image'
));
}
/**
* #return string
*/
public function getName() {
return 'mdb_platformbundle_image';
}
}
And I have the following error at the form submit :
Neither the property "images" nor one of the methods
"addImag()"/"removeImag()", "addImage()"/"removeImage()",
"setImages()", "images()", "__set()" or "__call()" exist and have
public access in class "MDB\AnnonceBundle\Entity\Annonce".
I don't understand cause these getter exist in my entity. Anyone have an idea?
Thanks
This error was logic. Just use a collection field type solved the problem
I got problem to save embedded collection form field. Let say I have program, and the program have many levels. In my form type:
ProgramType.php
<?php
namespace Eifl\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProgramType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('id','text',array(
'label'=>'Program Code',
))
->add('program','text',array(
'label'=>'Program Name',
))
->add('levels','collection', array(
'type'=>new LevelType(),
'allow_add'=>true,
'allow_delete'=>true,
'by_reference' => false,
))
->add('save','submit', array(
'label'=>'Save',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Eifl\AdminBundle\Entity\Program',
));
}
public function getName()
{
return 'Program';
}
}
LevelType.php
<?php
namespace Eifl\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class LevelType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('level','text',array(
'label'=>'Level Name',
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Eifl\AdminBundle\Entity\Level',
));
}
public function getName()
{
return 'level';
}
}
I made the form that can add a new collection level field using jquery, following the tutorial from this link, and the result of my form is same with the picture in this link.
Now my problem is I can't save more than 2 levels. The collection form only save 2 levels.
e.g. I want to save a program name as English, and after that I add 3 levels(basic,intermediate,advanced). The levels that saved successfully to database only the first level(basic) and the last level(Advanced). All I want is to save all the levels to database. Can anyone help me?
In addition, here is part of my controller:
DefaultController.php
public function programAction(Request $request)
{
$program = new Program();
$level = new Level();
$program->getLevels()->add($level);
$newProgram = $this->createForm(new ProgramType(),$program);
$newProgram->handleRequest($request);
if($newProgram->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($program);
$em->flush();
return $this->redirect($this->generateUrl('eifl_admin_program'));
}
return $this->render('EiflAdminBundle:Default:program.html.twig',array("new_program_form"=>$newProgram->createView()));
}
UPDATE
Here is my entity class.
Program.php
<?php
namespace Eifl\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Program
*
* #ORM\Entity
* #ORM\Table(name="tbl_program")
*/
Class Program
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="string")
*/
protected $id;
/**
*#ORM\Column(name="program_name",type="string")
*/
public $program;
/**
* #ORM\OneToMany(targetEntity="Eifl\AdminBundle\Entity\Level", mappedBy="program", cascade={"remove","persist"})
*/
public $levels;
public function __construct(){
$this->levels = new ArrayCollection();
}
/**
* #param string $id
* #return string
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* Add Level
*
* #param \Eifl\AdminBundle\Entity\Level $levels
* #return Program
*/
public function addLevels(\Eifl\AdminBundle\Entity\Level $level) {
$this->levels[] = $level;
$levels->setProgram($this);
return $this;
}
/**
* set levels
*
* #param \Doctrine\Common\Collections\ArrayCollection $levels
* #return levels
*/
public function setLevels(\Doctrine\Common\Collections\ArrayCollection $levels) {
foreach ($levels as $level) {
$level->setProgram($this);
}
$this->levels = $levels;
}
public function removeLevels(\Eifl\AdminBundle\Entity\Level $level)
{
$this->levels->removeElement($level);
}
/**
* #param string $program
* #return string
*/
public function setProgram($program)
{
$this->program = $program;
return $this;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getProgram()
{
return $this->program;
}
/**
* #return string
*/
public function getLevels()
{
return $this->levels;
}
}
Level.php
<?php
namespace Eifl\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Level
*
* #ORM\Entity
* #ORM\Table(name="tbl_level")
*/
Class Level
{
/**
* #ORM\Id
* #ORM\Column(name="id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*#ORM\Column(name="level",type="string")
*/
public $level;
/**
* #ORM\ManyToOne(targetEntity="Eifl\AdminBundle\Entity\Program", inversedBy="levels")
* #ORM\JoinColumn(name="program", referencedColumnName="id")
*/
public $program;
/**
* #param string $id
* #return string
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* #param string $level
* #return string
*/
public function setLevel($level)
{
$this->level = $level;
return $this;
}
/**
* #param mixed $program
* #return string
*/
public function setProgram(\Eifl\AdminBundle\Entity\Program $program)
{
$this->program = $program;
return $this;
}
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #return string
*/
public function getLevel()
{
return $this->level;
}
/**
* #return string
*/
public function getProgram()
{
return $this->program;
}
}
Check if you put addLevel and setLevel in your program entity
/**
* Add Level
*
* #param \Eifl\AdminBundle\Entity\Level $levels
* #return Program
*/
public function addLevel(\Eifl\AdminBundle\Entity\Level $levels) {
$this->levels[] = $levles;
$level->setProgram($this);
return $this;
}
/**
* set levels
*
* #param \Doctrine\Common\Collections\ArrayCollection $levels
* #return levels
*/
public function setLevels(\Doctrine\Common\Collections\ArrayCollection $levels) {
foreach ($levles as $level) {
$level->setPogram($this);
}
$this->levels = $levels;
}
remove this code from the controllor
$level = new Level();
$program->getLevels()->add($level);
Your getter and setter $levels in Entity Program are confused...
make :
public function addLevel(Level $level) { //without s
$this->levels[] = $level;
}
public function removeLevel(Level $level) //without s
{
$this->levels->removeElement($level);
}
public function getLevels()
{
return $this->levels;
}
Remove otherspublic function setLevels, public function removeLevels, etc
Like Adene Choubi said, $program->getLevels()->add($level); make no sense too.
Don't know if it's the only trouble but begin with that.