To be short
my entity have this:
/**
* #ORM\ManyToOne(targetEntity="Classes", inversedBy="")
* #ORM\JoinColumn(name="class_id", referencedColumnName="id")
*/
protected $class;
i have no inverse relation, because i don't need it.
and i try to do this:
$class = $this->getDoctrine()->getRepository('TomasRClassBundle:Classes')->find($_POST['fighter']['class']);
$item = new Fighter();
$item->setName($_POST['fighter']['name']);
$item->setClass($class);
//if i var_dump here $item->getClass(), its there, class where set correctly
$item->setBelongsTo(1);
$em = $this->getDoctrine()->getManager();
$em->persist($item);
$em->flush();
and after that in database, in class_id i have null.. however if I edit it with form, this is form field:
$builder->add('class', 'entity', array('class' => 'TomasRClassBundle:Classes','property' => 'name'));
and after i submit the form i do:
$item = $this->getDoctrine()->getRepository('TomasRClassBundle:Fighter')->find($id);
$type = new FighterForm();
$form = $this->createForm($type, $item);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($item);
$em->flush();
//return $this->redirect($this->generateUrl('admin_fighter_list'));
}
and now the class_id field in db is set correctly.
i could hard code my solution, but i wanna know what i am doing wrong?
Thanks in advance, i know it might be a bit hard to read, sorry for that :)
Related
I have already inquired here and there but nothing more or less corresponds to my problem.
I have a page with information about a movie, which I access with an id parameter:
See comments
The film table having a relation with the table how, I display all the comments specific to the movie thanks to an ArrayCollection :
$filmRepo = $repo->find($id);
$comments = $filmRepo->getComments();
I created a CommentController in which I wrote this method whose goal would be to recover the id of the movie AND the id of the comment in order to be able to make CRUD operations on it:
/**
* #Route("{id}/{comment}/create", name="createComment")
* #Route("{id}/{comment}/modif", name="modifComment", defaults={"comment"=1}, methods="GET|POST")
*/
public function modification(Comment $comment = null, Film $film, Request $req, EntityManagerInterface $em)
{
if(!$comment) {
$comment = new Comment();
}
$user = $this->getUser();
$form = $this->createForm(CommentType::class, $comment);
$form->handleRequest($req);
if($form->isSubmitted() && $form->isValid()) {
$comment->setAuthor($user);
$comment->setFilm($film);
$em->persist($comment);
$em->flush();
$this->addFlash('success', 'L\'action a bien été effectuée');
return $this->redirectToRoute('home');
}
return $this->render('comment/modif.html.twig', [
"comment" => $comment,
"form" => $form->createView()
]);
}
But no matter which comment I select, it takes the default comment, that is to say the one with id 1. So something is wrong with my request. However I pass the two parameters in the twig template:
Modif
The problem comes from a syntax error in the twig template. Instead of :
Modif
Rather do :
Modif
I am in the middle of converting a project from Symfony 2 to Symfony 3 and have run into a problem. I have an Event entity that has a TwitterSearch entity which is defined by the following in Event.php:
/**
* #ORM\ManyToOne(targetEntity="TwitterSearch", cascade={"persist"})
* #ORM\JoinColumn(name="twitter_search_id", referencedColumnName="id")
*/
private $twitterSearch;
This is how I have set up the relationship in the EventType:
$builder->add('twitterSearch', TwitterSearchType::class, array(
'required'=>false,
));
Here is the code from the controller for when the form is submitted:
$entity = new Event();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$entity = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
}
This used to work in Symfony 2 but now when the form is submitted the following error is thrown:
Expected value of type "TwitterSearch" for association field "Event#$twitterSearch"
, got "array" instead.
I debugged the code and the twitterSearch attribute of the Event was and array. Does anyone know why it is no longer converting this to an instance of TwitterSearch?
Use EntityType instead.
Example:
$builder->add('twitterSearch', EntityType::class, array(
'class' => TwitterSearch::class,
'choice_label' => 'name',
));
I'm quite new here, be patient, please.
I'm trying to make notice board project in Symfony2 using FOSUserBundle.
I try to get logged user id to put it into form created with form builder (and then to MySQL database).
One of attempts is:
public function createNoticeAction(Request $request)
{
$notice = new Notice();
$form = $this->createFormBuilder($notice)
->add("content", "text")
->add("user_id","entity",
array("class"=>"FOS/UserBundle/FOSUserBundle:", "choice_label"=>"id"))
->add("isActive", "true")
->add("category", "entity",
array("class" => "AppBundle:Category", "choice_label" => "name"))
->add("save", "submit", array("label" => "Save"))
->getForm();
$form->handleRequest($request);
$em = $this->getDoctrine()->getManager();
$em->persist($notice);
$em->flush();
return $this->redirectToRoute('app_user_showuserpage');
}
I tried many solutions again and again and I get some error.
You already have the user object Symfony > 2.1.x
In you Controller like this:
$userId = $this->getUser()->getId();
...
$notice->setUserId($userId);
$em->persist($notice);
Don't ->add field in you FormBuilder, its not safely. Set this value in you Controller and don't ->add this field in FormBuilder
for symfony 3.2.13
have excelent solution (just because is working, but is dangerous if someone discover it in pure HTML)
1) first build YourFormType class.
add normal field in Forms/YourFormType.php (if not, formbuilder tell you that you passing smth not quite right (too many fields) ; -) )
$builder
->add(
'MyModelAddedById',
HiddenType::class,
[
'label' => 'echhh', //somehow it has to be here
'attr' => ['style' => 'display:none'], //somehow it has to be here
]
);
2) in your controller
public function addSomethingAction(Request $request){
$form = $this->createForm(MyModelFormType::class);
//set field value
$request->request->set("prodModelAddedById", $this->getUser()->getId());
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$product = $form->getData();
$em = $this->getDoctrine()->getManager();
$em->persist($product);
$em->flush();
$this->addFlash('success', 'record was added');
return $this->redirectToRoute('products');
}
return $this->render(
'default.add.form.html.twig',
[
'newprod' => $form->createView(),
]
);
}
explenation:
you are passing a field and variable to formbuilder (settig it already to default value!)
and important thing, becose of BUG in my opinion - you can't in your form type set method:
public function getBlockPrefix()
{
//return 'app_bundle_my_form_type';
}
because
$request->request->set
can't work properly if your POST data from form are in bag (parameterbag)
no entity managers, no services, no listeners...
hope it helps.
I have problem saving entity trough form with ManyToMany relations.
I can not save fields that are on "mappedBy" side of relation.
Code below is not saving anything to database and not trowing any errors:
// Entity/Pet
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Customer", mappedBy="pet", cascade={"persist"})
*/
private $customer;
/**
* Set customer
*
* #param \AppBundle\Entity\Customer $customer
* #return Pet
*/
public function setCustomer($customer)
{
$this->customer = $customer;
return $this;
}
// Entity/Customer
/**
* #var Pet
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Pet", inversedBy="customer", cascade={"persist"})
* #ORM\JoinTable(name="customer_pet",
* joinColumns={
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="pet_id", referencedColumnName="id")
* }
* )
*/
private $pet;
// PetType.php
$builder->add('customer', 'entity',
array(
'class' => 'AppBundle:Customer',
'property' => 'firstname',
'empty_value' => 'Choose owner',
'multiple' => true
));
It is working the other way around. So if I am saving something from CustomerType everything works.
EDIT:
Solution below worked for me but after couple days I found a problem with that solution. If form will be submitted with value that has been already saved in the database then Symfony will trow an error. To prevent that I had to check if given customer has been already assigned to the pet.
Checking of currently assigned customers had to be done on the beginning of function and not after form submission because for some reason after submission Pet() object contains submitted values not only those present in the db.
So on the beginning I've putted all already assigned customers in to the array
$em = $this->getDoctrine()->getManager();
$pet = $em->getRepository('AppBundle:Pet')->find($id);
$petOriginalOwners = array();
foreach ($pet->getCustomer() as $petCustomer)
{
$petOriginalOwners[] = $petCustomer->getId();
}
And after form submission I've checked if submitted ID's are in the array
if ($form->isValid())
{
foreach ($form['customer']->getData()->getValues() as $v)
{
$customer = $em->getRepository('AppBundle:Customer')->find($v->getId());
if ($customer && !in_array($v->getId(), $petOriginalOwners) )
{
$customer->addPet($pet);
}
}
$em->persist($pet);
$em->flush();
return $this->redirect($this->generateUrl('path'));
}
In Symfony2 the entity with the property with the inversedBy doctrine comment is the one that is supposed to EDIT THE EXTRA TABLE CREATED BY THE MANYTOMANY RELATION. That is why when you create a customer it inserts the corresponding rows in that extra table, saving the corresponding pets.
If you want the same behavior to happen the other way around, I recommend:
//PetController.php
public function createAction(Request $request) {
$entity = new Pet();
$form = $this->createCreateForm($entity);
$form->submit($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
foreach ($form['customer']->getData()->getValues() as $v) {
$customer = $em->getRepository('AppBundle:Customer')->find($v->getId());
if ($customer) {
$customer->addPet($entity);
}
}
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('pet_show', array('id' => $entity->getId())));
}
return $this->render('AppBundle:pet:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
private function createCreateForm(Pet $entity) {
$form = $this->createForm(new PetType(), $entity, array(
'action' => $this->generateUrl('pet_create'),
'method' => 'POST',
));
return $form;
}
These two are but standard Symfony2 CRUD-generated actions in the controller corresponding to Pet entity.
The only tweak is the foreach structure inserted in the first action, that way you forcibly add the same pet to each customer you select in the form, thus getting the desired behavior.
Look, it is highly probable THIS is not the RIGHT WAY, or the PROPER WAY, but is A WAY and it works. Hope it helps.
In my case with a services <-> projects scenario, where services has "inversedBy" and projects has "mappedBy" I had to do this in my project controller's edit action so that when editing a project the services you checked would be persisted.
public function editAction(Request $request, Project $project = null)
{
// Check entity exists blurb, and get it from the repository, if you're inputting an entity ID instead of object ...
// << Many-to-many mappedBy hack
$servicesOriginal = new ArrayCollection();
foreach ($project->getServices() as $service) {
$servicesOriginal->add($service);
}
// >> Many-to-many mappedBy hack
$form = $this->createForm(ProjectType::class, $project);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
// << Many-to-many mappedBy hack
foreach ($servicesOriginal as $service) {
if (!$project->getServices()->contains($service)) {
$service->removeProject($project);
$em->persist($service);
}
}
foreach ($project->getServices() as $service) {
$service->addProject($project);
$em->persist($service);
}
// >> Many-to-many mappedBy hack
$em->persist($project);
$em->flush();
return; // I have a custom `redirectWithMessage()` here, use what you like ...
}
return $this->render("Your-template", [
$form => $form->createView(),
$project => $project,
]);
}
This works for both adding and removing entities in the many-to-many from the "mappedBy" side, so EntityType inputs should work as intended.
What's going on here is we're first building an "original" collection containing all of the service entities already linked to for this project. Then when the form is saving we're ensuring:
First that any unchecked services (those in the original collection but not the project object) have the project removed from their internal collection, then persisted.
Second that any newly checked services each add the project to their internal collection, then persisted.
Important: This depends on your entity's addService() and addProject() methods respectively check that each others' collections do not contain duplications. If you don't do this you'll end up with an SQL level error about a duplicate record insertion.
In the service entity I have:
/**
* Add project
*
* #param Project $project
*
* #return Service
*/
public function addProject(Project $project)
{
if (!$this->projects->contains($project)) {
$this->projects->add($project);
}
if (!$project->getServices()->contains($this)) {
$project->getServices()->add($this);
}
return $this;
}
In the project entity I have:
/**
* Add service
*
* #param Service $service
*
* #return Project
*/
public function addService(Service $service)
{
if (!$this->services->contains($service)) {
$this->services->add($service);
}
if (!$service->getProjects()->contains($this)) {
$service->getProjects()->add($this);
}
return $this;
}
You could alternatively check this in your controller instead, but makes sense if the model validates this itself when possible, as the model would break anyway if there were duplicates from any source.
Finally in your controller's create action you'll likely need this bit too just before $em->persist($project). (You won't need to work with an "original" collection as none will exist yet.)
// << Many-to-many mappedBy hack
foreach ($project->getServices() as $service) {
$service->addProject($project);
$em->persist($service);
}
// >> Many-to-many mappedBy hack
I just had the same problem and I solved it differently.
Changing the code in the controller is not the better way to do it.
In my case I have a GenericController that handle all my CRUDs so I can't put in it specific code.
The best way to do it is by adding in your PetType a listener like this :
// PetType.php
$builder->add('customer', 'entity',
array(
'class' => 'AppBundle:Customer',
'property' => 'firstname',
'empty_value' => 'Choose owner',
'multiple' => true
))
->addEventListener( FormEvents::SUBMIT, function( FormEvent $event ) {
/** #var Pet $pet */
$pet = $event->getData();
foreach ( $pet->getCustomers() as $customer ) {
$customer->addPet( $pet );
}
} );
That way you'll keep the mapping logic in the same place.
I have this forms + controllers http://pastebin.com/wLPyXvbj
They are working when I'm adding data to DB (new data) but I have troubles while editing, I explain here the best I can.
Case 1
I edit EVERY SINGLE FIELD, data is updated on DB OK.
Case 2
I try to edit FEW fields, only fields I want users to edit instead of editing everything. Fields that have been available for update are inserted into DB without problem, but the other fields, thoose which I didn't want users to modify instead of keeping the old values in DB (because they HAVEN'T changed), they all turn empty values.
So there is something wrong with "selective editing" instead of "all fields edit" but I can't find the problem... I can't make an all fields edit form because some of them are static, no edits.
This is the controller I'm using for the edit action, as you can see it is diferent from the newAction
public function agentupdateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$comments = new VtigerTicketcomments();
$session = $this->get("session");
$proyecto = $session->get('proyecto');
$assets = $session->get('assets');
$contacts = $em->createQuery('SELECT u.contactid, u.email, u.phone, u.mobile FROM WbsGoclientsBundle:VtigerContactdetails u')->getResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
foreach($assets as $a)
$uuids[$a['UUID']] = $a['UUID'];
$entity = $em->getRepository('WbsGoclientsBundle:VtigerTicketcf')->find($id);
$ticket = $entity->getId();
$solution = $ticket->getSolution();
$ticketcfForm = $this->createForm(new TicketcfType($uuids, $session->get('contacts'), $session->get('rol'), $session->get('tecnicos')), $entity);
$ticketcfForm->submit($request);
if($ticketcfForm->isValid())
{
$data = $ticketcfForm->getData();
if($ticket->getStatus() == 'Closed')
{
if(!$ticket->getSolution())
$ticket->setSolution($solution);
$workflow = new ComVtigerWorkflowtaskQueue();
$workflow->setTaskid('9');
$workflow->setEntityid('9x'.$id);
$workflow->setDoafter('0');
$em->persist($workflow);
$em->flush();
$hoy = new \DateTime();
if($ticketcf->getFReso() == null)
$ticketcf->setFReso($hoy->format('d-m-Y H:i:s'));
}
$em->persist($ticket);
$em->flush();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('tickets_show', array('id' => $ticket->getTicketNo())));
}
return $this->render('WbsGoclientsBundle:Tickets:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'rol' => $session->get('rol'),
'ticket_form' => $ticketForm->createView(),
));
}