I have 2 entities that are related with a OneToOne relationship. I embeded the inverse side into the form, and everything works fine, except that the link between the relationship is not stored.
So: The "Begeleider" is saved and the "CompetentieProfiel" is saved, but the column that references the "Begeleider" inside "CompetentieProfiel" table is null.
Ath the moment flush() is called, the "Begeleider" object has the "CompetentieProfiel" object as variable.
Contact:
class Contact {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
}
Begeleider:
class Begeleider extends Contact {
/**
* #ORM\OneToOne(targetEntity="CompetentieProfiel", mappedBy="begeleider" ,cascade={"persist"})
*/
private $competentieProfiel;
}
CompetentieProfiel:
class CompetentieProfiel {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="Begeleider", inversedBy="competentieProfiel",cascade={"persist"})
*/
protected $begeleider;
}
Form:
class BegeleiderType extends AbstractType {
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('competentieProfiel', new CompetentieProfielType());
}
Controller:
public function createAction(Request $request) {
$entity = new Begeleider();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
// Return the ok status and the begeleider html.
$response = new Response(
json_encode(
array(
'status' => 'ok',
)
)
);
$response->headers->set('Content-Type', 'application/json');
return $response;
}
While you are associating the object from one side you are not from the other. So A is associated with B but B isn't associated with A, if that makes any sense.
From what I know the best way is to add a check in your setter to set the associating object like so.
In Aaaa
public function setBbbb(Bbbb $bbbb)
{
if (null === $bbbb->getAaaa() || $this !== $bbbb->getAaaa()) {
$bbbb->setAaaa($this);
}
$this->bbbb = $bbbb;
return $this;
}
In Bbbb
public function setAaaa(Aaaa $aaaa)
{
if (null === $aaaa->getBbbb() || $this !== $aaaa->getBbbb()) {
$aaaa->setBbbb($this);
}
$this->aaaa = $aaaa;
return $this;
}
This way when either of the sides are set then the other side is automatically set too.
Related
I'm currently working on something like a survey/quiz in Symfony. To do so, I have a Question-Entity with an Description, and a Quiz-Entity, which has nothing special unity know except of a submit date.
The important thing is that I want to generate a dynamic form with a random dataset of questions from the db. My current FormType looks like this:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
$questions = $event->getData();
$form = $event->getForm();
$i = 0;
foreach($questions as $question) {
$form
->add('question'.$i, TextareaType::class,[
'label' => $question['description'],
'mapped' => false,
])
;
$i++;
}
$form->add('submit', SubmitType::class);
});
}
My controller:
/**
* #Route("/new", name="new")
*/
public function newQuiz(Request $request)
{
$result = $this->quizQuestionRepository->getRandomQuestion();
$form = $this->createForm(QuestionFormType::class, $result);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
dd($data);
}
return $this->render('question/new.html.twig', [
'form' => $form->createView()
]);
}
The Question-Entity:
/**
* #ORM\Entity()
* #ORM\Table(name="questions")
*/
class Question
{
/**
* #ORM\Id()
* #ORM\Column(type="uuid", unique=true)
*/
private string $id;
/**
* #ORM\Column(type="text")
*/
private string $description;
/**
* #ORM\Column(type="string")
*/
private string $category;
public function __construct()
{
$this->id = Uuid::uuid4()->toString();
}
// GETTERS AND SETTERS - NOTHING SPECIAL
}
But this doesn't work. For some reason, I get the questions objects returned, not the insertions from the textareas. Can someone help me out?
I have set the relationship on the entity's to set many customers to a user entity as a collection and added Multiple to the form field...it's posting ok it's just not updating the user_id in the customer table but it was when using OneToOne relation. Any help would be appreciated.
User entity code
/**
* #var Customer[]
* #ORM\OneToMany(targetEntity="App\Entity\Customer", mappedBy="user", cascade={"all"})
* #ORM\JoinColumn(nullable=true)
*/
private $customer;
public function __construct()
{
$this->staffUsers = new ArrayCollection();
$this->customer = new ArrayCollection();
}
/**
* #param Collection|null $customer
* #return $this
*/
public function setCustomer(?Collection $customer): self
{
$this->customer = $customer;
return $this;
}
Customer entity code
/**
* #ORM\ManyToOne(targetEntity="App\Entity\User", inversedBy="customer", cascade={"all"})
*/
private $user;
/**
* #return User|null
*/
public function getUser(): ?User
{
return $this->user;
}
/**
* #param User|null $user
* #return $this
*/
public function setUser(?User $user): self
{
$this->user = $user;
return $this;
}
Controller code
public function newUser(Request $request, UserPasswordEncoderInterface $encoder) : Response
{
/** #var UserRepository $userRepo */
$userRepo = $this->getDoctrine()->getRepository(User::class);
$customer = new Customer();
// make form
$form = $this->createForm(UserType::class,new User());
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()){
/** #var User $newUser */
$newUser = $form->getData();
// dump($newUser);
// die();
// hold user roles
$roles = ['ROLE_USER'];
// check if admin role
$adminRole = (bool)$form->get('adminRole')->getData();
if($adminRole){
$roles[]='ROLE_ADMIN';
}
// is a customer selected?
if($newUser->getCustomer() && $newUser->getCustomer()->count() > 0){
$roles[]='ROLE_CUSTOMER';
}
$newUser->setRoles($roles);
// encode pw
$newUser->setPassword(
$encoder->encodePassword($newUser,$newUser->getPassword())
);
// create
$userRepo->insert($newUser);
return $this->redirectToRoute('usersListing');
}
return $this->render('admin/users/user-form.html.twig',[
'form'=>$form->createView()
]);
}
Customer entity type on User form
->add('customer',EntityType::class,[
'required'=>false,
'multiple' => true,
'attr'=>[
'class'=>'selectpicker form-control',
'multiple' =>'multiple',
'data-width' => "100%"
],
'label'=>'Customer(s)',
'placeholder'=>'N/A',
'class'=>Customer::class,
'query_builder'=>function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->orderBy('c.lname', 'ASC')
->orderBy('c.fname','ASC');
},
'constraints'=>[
new Callback(function(?Collection $customers, ExecutionContextInterface $context) use($userRepo){
// check if the customer is already linked to a user
if($customers && $customers->count() > 0){
/** #var Customer $customer */
foreach($customers as $customer){
if($customer->getUser()){
$context->addViolation('Customer Is Already Linked To User: ' . $customer->getUser()->getUsername());
return false;
}
}
}
return true;
})
]
])
Rename property customer to customers and function from setCustomer to setCustomers, you should also create an addCustomer method in your User class:
public function addCustomer(Customer $customer)
{
$this->customers[] = $customer;
$customer->setUser($this); // sets the owning side, without this your will end up with user_id equal to null
}
And whenever you want to add a customer you just invoke the addCustomer method.
If you want to use the setCustomers method make sure you set the user in your customer entity.
I have a little problem with my image upload, can u help me please:
Could not determine access type for property "file".
Controller
/**
* Creates a new Produits entity.
*
*/
public function createAction(Request $request)
{
$entity = new Produits();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('adminProduits_show', array('id' => $entity->getId())));
}
return $this->render('EcommerceBundle:Administration:Produits/layout/new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
/**
* Creates a form to create a Produits entity.
*
* #param Produits $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createCreateForm(Produits $entity)
{
$form = $this->createForm(ProduitsType::class, $entity);
$form->add('submit', SubmitType::class, array('label' => 'Ajouter'));
return $form;
}
/**
* Displays a form to create a new Produits entity.
*
*/
public function newAction()
{
$entity = new Produits();
$form = $this->createCreateForm($entity);
return $this->render('EcommerceBundle:Administration:Produits/layout/new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Form
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class, array('data_class' => null))
->add('name', TextType::class)
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ecommerce\EcommerceBundle\Entity\Media'
));
}
/**
* #return string
*/
public function getName()
{
return 'ecommerce_ecommercebundle_media';
}
Entity
/**
* #ORM\Column(name="name",type="string",length=255)
* #Assert\NotBlank()
*/
private $name;
/**
* #ORM\Column(type="string",length=255, nullable=true)
*/
private $path;
/**
* #Assert\File(
* maxSize = "1024k",
* mimeTypes = {"image/png", "image/jpg", "image/bmp"},
* mimeTypesMessage = "Please upload a valid PDF"
* )
*/
public $file;
public function getUploadRootDir()
{
return __dir__.'/../../../../web/uploads';
}
public function getAbsolutePath()
{
return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path;
}
public function getAssetPath()
{
return 'uploads/'.$this->path;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
$this->tempFile = $this->getAbsolutePath();
$this->oldFile = $this->getPath();
$this->updateAt = new \DateTime();
if (null !== $this->file)
$this->path = sha1(uniqid(mt_rand(),true)).'.'.$this->file->guessExtension();
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null !== $this->file) {
$this->file->move($this->getUploadRootDir(),$this->path);
unset($this->file);
if ($this->oldFile != null) unlink($this->tempFile);
}
}
/**
* #ORM\PreRemove()
*/
public function preRemoveUpload()
{
$this->tempFile = $this->getAbsolutePath();
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if (file_exists($this->tempFile)) unlink($this->tempFile);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getPath()
{
return $this->path;
}
public function getName()
{
return $this->name;
}
public function getFile()
{
return $this->file;
}
/**
* Set path
*
* #param string $path
* #return String
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Set alt
*
* #param string $alt
* #return String
*/
public function setAlt($alt)
{
$this->alt = $alt;
return $this;
}
/**
* Set name
*
* #param string $name
* #return String
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Set updateAt
*
* #param \DateTime $updateAt
*
* #return Media
*/
public function setUpdateAt($updateAt)
{
$this->updateAt = $updateAt;
return $this;
}
/**
* Get updateAt
*
* #return \DateTime
*/
public function getUpdateAt()
{
return $this->updateAt;
}
Thanks for your help guys :)
Please add "'mapped' => false," to form builder.
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('file', FileType::class,
array(
'data_class' => null,
'mapped' => false,
))
->add('name', TextType::class)
;
}
Those who say that it is wrong to dissolve, see the test there. I'm not the one who made it wrong.
Test code:
https://github.com/symfony/property-access/blob/master/Tests/PropertyAccessorCollectionTest.php#L151
A second solution is to add function setXxx to the property that gives an error in class Entity.
public $xxx;
public function setXxx(Array $xxx)
{
$this->xxx = $xxx;
}
Or
public function __construct()
{
$this->xxx = new ArrayCollection();
}
Video Link: https://knpuniversity.com/screencast/doctrine-relations/create-genus-note
My English is bad, I can tell you.
Setting mapped => false is not the real solution, because the field IS mapped to the entity.
In my case, I have a OneToMany relation, so I need the field mapped in order to use the 'cascase' => {"all"} option.
If the field is not mapped, then you must to persist the related entity manually. That's no good.
Stumbled on this while searching solution for my problem, that was shooting the same error and I was using also ArrayCollection class, so this may be helpful to someone:
My field in ArrayCollection is called $files, so I had to add constructor like this:
use Doctrine\Common\Collections\ArrayCollection;
public function __construct()
{
$this->files = new ArrayCollection();
}
Then I added add and remove methods, like this:
public function addFile(MyFile $file) : self
{
$file->setParentMyItem($this); // Setting parent item
$this->files->add($file);
return $this;
}
public function removeFile(MyFile $file) : self
{
$this->files->removeElement($file);
return $this;
}
But catch is that even my field name was $files I had to name add and remove methods addFile() and removeFile(), without 's' at end, which is totally not logical to me, but that solved the problem.
Same error for me but on a OneToMany relation. Although setting "mapped => false" also solved the error there is another option. I only had a getter, adder and remover method on the entity. I needed to add a "setter" too
In my case it was:
/**
* #param ArrayCollection|SubStatusOptions[]
*/
public function setSubStatusOptions(ArrayCollection $subStatusOptions)
{
$this->subStatusOptions = $subStatusOptions;
}
This happened because I had an entity property but the getter/setters were missing. I used:
php bin/console make:entity --regenerate MyBundle\\Entity\\MyEntity
to regenerate them
How to implement NotifyPropertyChanged for retrieving entity changes (in Symfony 2.8 project with doctrine)?
I have found several examples, but they are not full:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/change-tracking-policies.html
http://www.boostr.in/26896/is-there-a-built-in-way-to-get-all-of-the-changed-updated-fields-in-a-doctrine-2%23googleads
For me it is not clear if i have to implement the own onPropertyChanged function, how ? I would like to retrive the property changes naming the last and the current properties in the controller and format them as a message for the user.
When i search in the documentation, NotifyPropertyChanged is not described or described minimally:
http://symfony2-document.readthedocs.io/en/latest/search.html?q=NotifyPropertyChanged&check_keywords=yes&area=default
http://api.symfony.com/2.8/search.html?search=NotifyPropertyChanged
http://docs.w3cub.com/symfony~2.8/
http://phpdox.de/demo/Symfony2/interfaces/Doctrine_Common_NotifyPropertyChanged.xhtml
It is written that addPropertyChangedListener() — Adds a listener that wants to be notified about property changes. I do not understand how i should put the custom code in this listener and how to execute this listener in such a way, that i could retrieve the entity changes to the controller action.
// src\MeetingBundle\Entity\EventLisObject.php
<?php
namespace MeetingBundle\Entity;
use Doctrine\Common\NotifyPropertyChanged;
use Doctrine\Common\PropertyChangedListener;
abstract class EventLisObject implements NotifyPropertyChanged
{
private $listeners = array();
public function addPropertyChangedListener(PropertyChangedListener $listener) {
$this->listeners[] = $listener;
}
/** Notifies listeners of a change. */
protected function onPropertyChanged($propName, $oldValue, $newValue) {
if ($this->listeners) {
foreach ($this->listeners as $listener) {
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
}
}
}
}
//C src\MeetingBundle\Entity\Event.php
<?php
namespace MeetingBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Event
*
* #ORM\Table(name="tevent", indexes={#ORM\Index(columns={"keywords"}, flags={"fulltext"})})
* #ORM\Entity(repositoryClass="MeetingBundle\Repository\EventRepository" )
*/
class Event extends EventLisObject
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=255)
* #Assert\NotBlank()
*/
private $title;
/**
* Set title
*
* #param string $title
*
* #return Event
*/
public function setTitle($title) {
// $this->title = $title;
if( $title != $this->title ) {
$this->_onPropertyChanged("title", $this->title, $title);
$this->title = $title; }
return $this;
}
// src\MeetingBundle\Controller\EventMapControllerACL.php
....
/**
* Event controller.
*
* #Route("/eventmapacl")
*/
class EventMapControllerACL extends Controller
{
/**
* #Route("/edit/{id}", name="event_jsMap_edit")
* #Method("GET|POST")
* #Template("MeetingBundle:Event:ev_jsMap_edit.html.twig")
*/
public function editAction($id, Request $request)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('MeetingBundle:Event')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Event entity.');
}
$editForm = $this->createEditForm($entity);
$editForm->handleRequest($request);
// $deleteForm = $this->createDeleteForm($id);
if ($editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
// here or before flush or before persist i would like to estimate the changes and generate the message to user informing about them.
return $this->redirect($this->generateUrl('event_jsMap_edit', array('id' => $entity->getId())));
}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
);
}
I have a form for my "Team" entity. This entity has an "image" field. This field is required on creation process, but not required on edit process. But right now, during edit process, if I don't provide any image in the file input, the empty input is still persisted, and so my database field is emptied during the process.
How can I do to avoid persistence of this field, if nothing is provided in the form file input? So the entity keeps its old value for this field. Of course, if a file is provided, I want him to erase the old one.
My controller looks like this:
if ($request->getMethod() == 'POST') {
$form->bind($request);
if ($form->isValid()) {
$em->persist($team);
$em->flush();
...
}
}
and part of my entity, dealing with image (I'm pretty sure I have to do something in here, but don't know what exactly):
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function uploadImage() {
// the file property can be empty if the field is not required
if (null === $this->image) {
return;
}
if(!$this->id){
$this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
}else{
$this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
}
$this->setImage($this->image->getClientOriginalName());
}
EDIT
Ok, I did some changes to this answer's code code, because apparently the event listener asks for a FormEvent instance in his callback, not a FormInterface instance.
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
// Retrieve submitted data
$form = $event->getForm();
$item = $event->getData();
// Test if upload image is null (maybe adapt it to work with your code)
if (null !== $form->get('image')->getData()) {
var_dump($form->get('image')->getData());
die('image provided');
$item->setImage($form->get('image')->getData());
}
});
When I provide an image, the script goes into the test, and die(), as expected. When I do not provide any file, the script doesn't go into the test if(), but my field in the database is still erased with an empty value. Any idea?
And as asked below, here is the Form
// src/Van/TeamsBundle/Form/TeamEditType.php
namespace Van\TeamsBundle\Form;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class TeamEditType extends TeamType // Ici, on hérite de ArticleType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// On fait appel à la méthode buildForm du parent, qui va ajouter tous les champs à $builder
parent::buildForm($builder, $options);
// On supprime celui qu'on ne veut pas dans le formulaire de modification
$builder->remove('image')
->add('image', 'file', array(
'data_class' => null,
'required' => false
))
;
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
// Retrieve submitted data
$form = $event->getForm();
$item = $event->getData();
// Test if upload image is null (maybe adapt it to work with your code)
if (null !== $form->get('image')->getData()) {
var_dump($form->get('image')->getData());
die('image provided');
$item->setImage($form->get('image')->getData());
}
});
}
// On modifie cette méthode car les deux formulaires doivent avoir un nom différent
public function getName()
{
return 'van_teamsbundle_teamedittype';
}
}
and the whole Team entity:
<?php
namespace Van\TeamsBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Team
*
* #ORM\Table()
* #ORM\HasLifecycleCallbacks
* #ORM\Entity
* #ORM\Entity(repositoryClass="Van\TeamsBundle\Entity\TeamRepository") #ORM\Table(name="van_teams")
*/
class Team
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="countryCode", type="string", length=2)
*/
private $countryCode;
/**
* #ORM\ManyToOne(targetEntity="Van\TeamsBundle\Entity\Game")
* #ORM\JoinColumn(nullable=false)
*/
private $game;
/**
* #ORM\ManyToOne(targetEntity="Van\TeamsBundle\Entity\Statut")
* #ORM\JoinColumn(nullable=false)
*/
private $statut;
/**
* #var string $image
* #Assert\File( maxSize = "1024k", mimeTypesMessage = "Please upload a valid Image")
* #ORM\Column(name="image", type="string", length=255)
*/
private $image;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Team
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set countryCode
*
* #param string $countryCode
* #return Team
*/
public function setCountryCode($countryCode)
{
$this->countryCode = $countryCode;
return $this;
}
/**
* Get countryCode
*
* #return string
*/
public function getCountryCode()
{
return $this->countryCode;
}
/**
* Set image
*
* #param string $image
* #return Team
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set game
*
* #param \Van\TeamsBundle\Entity\Game $game
* #return Team
*/
public function setGame(\Van\TeamsBundle\Entity\Game $game)
{
$this->game = $game;
return $this;
}
/**
* Get game
*
* #return \Van\TeamsBundle\Entity\Game
*/
public function getGame()
{
return $this->game;
}
/**
* Set statut
*
* #param \Van\TeamsBundle\Entity\Statut $statut
* #return Team
*/
public function setStatut(\Van\TeamsBundle\Entity\Statut $statut)
{
$this->statut = $statut;
return $this;
}
/**
* Get statut
*
* #return \Van\TeamsBundle\Entity\Statut
*/
public function getStatut()
{
return $this->statut;
}
public function getFullImagePath() {
return null === $this->image ? null : $this->getUploadRootDir(). $this->image;
}
protected function getUploadRootDir() {
// the absolute directory path where uploaded documents should be saved
// return $this->getTmpUploadRootDir();
return __DIR__ . '/../../../../web/uploads/';
}
protected function getTmpUploadRootDir() {
// the absolute directory path where uploaded documents should be saved
return __DIR__ . '/../../../../web/uploads_tmp/';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function uploadImage() {
// the file property can be empty if the field is not required
if (null === $this->image) {
return;
}
if(!$this->id){
$this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
}else{
$this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
}
$this->setImage($this->image->getClientOriginalName());
}
/**
* #ORM\PostPersist()
*/
public function moveImage()
{
if (null === $this->image) {
return;
}
if(!is_dir($this->getUploadRootDir())){
mkdir($this->getUploadRootDir());
}
copy($this->getTmpUploadRootDir().$this->image, $this->getFullImagePath());
unlink($this->getTmpUploadRootDir().$this->image);
}
/**
* #ORM\PreRemove()
*/
public function removeImage()
{
unlink($this->getFullImagePath());
rmdir($this->getUploadRootDir());
}
}
EDIT 2
I did that. When I provide an image, it is saved in the image field in the database and redirection to my index page occurs. When I don't provide any image, redirection doesn't occur, and the following message appears above my file input in my form: "The file could not be found." In my TeamEditType class, I did the following, so the image should not be required.
$builder->remove('image')
->add('image', 'file', array(
'data_class' => null,
'required' => false
))
;
As of Symfony 2.3, you can simply use the PATCH http method, as documented here.
$form = $this->createForm(FooType::class, $foo, array(
'action' => $this->generateUrl('foo_update', array('id' => $foo->getId())),
'method' => 'PATCH',
));
It's an easy way to make a partial update of an entity using your main form, without rendering all the fields.
One approach in Symfony 2.4 (further information in Symfony2 coockbook) :
public function buildForm(FormBuilderInterface $builder, array $options)
{
// $builder->add() ...
$builder->addEventListener(FormEvents::POST_SUBMIT, function (FormInterface $form) {
// Retrieve submitted data
$form = $event->getForm();
$image = $form->getData();
// Test if upload image is null (maybe adapt it to work with your code)
if (null !== $form->get('uploadImage')->getData()) {
$image->setUploadImage($form->get('uploadImage')->getData());
}
});
}
Edit
It seems that you already test your prepersit data. Try the following :
public function setImage($image)
{
if($image !== null) {
$this->image = $image;
return $this;
}
}
I'm using symfony 3.3 and faced the same issue, below is the solution how I overcome it.
You need to create a extra property for image uploading in your entity other than image property, something like $file;
Product.php
/**
* #var string
*
* #ORM\Column(name="image", type="string")
*
*/
private $image;
/**
*
* #Assert\File(mimeTypes={ "image/jpeg", "image/jpg", "image/png" })
*/
private $file;
public function setFile($file)
{
$this->file = $file;
return $this;
}
public function getFile()
{
return $this->file;
}
// other code i.e image setter getter
...
ProductType.php
$builder->add('file', FileType::class, array(
'data_class' => null,
'required'=>false,
'label' => 'Upload Image (jpg, jpeg, png file)')
);
Form.html.twig
<div class="form-group">
{{ form_label(form.file) }}
{{ form_widget(form.file, {'attr': {'class': 'form-control'}}) }}
</div>
Finally ProductController.php
...
if ($form->isSubmitted() && $form->isValid()) {
$file = $item->getFile();
if($file instanceof UploadedFile) {
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move(
$this->getParameter('upload_directory'),
$fileName
);
$item->setImage($fileName);
}
...
}
...
Little more about upload_directory
app/config/config.html
parameters:
upload_directory: '%kernel.project_dir%/web/uploads'
Create a uploads directory in web dir.
I resolved this issue by using PHP Reflection API. My approach was to browse the attributes of the entity class and replace the values not provided in the query with those already saved.
/**
* #param \ReflectionClass $reflectionClass
* #param $entity
* #param Request $request
* #return Request
*/
public function formatRequest($class, $entity, Request $request){
$reflectionClass = new \ReflectionClass('AppBundle\Entity\\'.$class);
foreach ($reflectionClass->getProperties() as $attribut)
{
$attribut->setAccessible(true); // to avoid fatal error when trying to access a non-public attribute
if($request->request->get($attribut->getName()) == null) { // if the attribute value is not provided in the request
$request->request->set($attribut->getName(), $attribut->getValue($entity));
}
}
return $request;
}
And then I use it like this :
$request = $this->formatRequest("EntityName", $entity, $request);
This is really generic.