I am trying to create a form using Symfony and Doctrine.
I created a Job class, and a table in mysql which relates to it, using Doctrine. It also made the JobType and JobController, and Routing facility.
I can access the index page, where the jobs are listed, but can't access the new entry page.
Here are the files used for creating the forms.
JobController.php
<?php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use AppBundle\Entity\Job;
use AppBundle\Form\JobType;
/**
* Job controller.
*
* #Route("/job")
*/
class JobController extends Controller
{
/**
* Lists all Job entities.
*
* #Route("/", name="job_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$jobs = $em->getRepository('AppBundle:Job')->findAll();
return $this->render('job/index.html.twig', array(
'jobs' => $jobs,
));
}
/**
* Creates a new Job entity.
*
* #Route("/new", name="job_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request)
{
$job = new Job();
$jobType = new JobType();
$form = $this->createForm($jobType, $job);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($job);
$em->flush();
return $this->redirectToRoute('job_show', array('id' => $job->getId()));
}
return $this->render('job/new.html.twig', array(
'job' => $job,
'form' => $form->createView(),
));
}
/**
* Finds and displays a Job entity.
*
* #Route("/{id}", name="job_show")
* #Method("GET")
*/
public function showAction(Job $job)
{
$deleteForm = $this->createDeleteForm($job);
return $this->render('job/show.html.twig', array(
'job' => $job,
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing Job entity.
*
* #Route("/{id}/edit", name="job_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Job $job)
{
$deleteForm = $this->createDeleteForm($job);
$editForm = $this->createForm(new JobType(), $job);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($job);
$em->flush();
return $this->redirectToRoute('job_edit', array('id' => $job->getId()));
}
return $this->render('job/edit.html.twig', array(
'job' => $job,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a Job entity.
*
* #Route("/{id}", name="job_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, Job $job)
{
$form = $this->createDeleteForm($job);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($job);
$em->flush();
}
return $this->redirectToRoute('job_index');
}
/**
* Creates a form to delete a Job entity.
*
* #param Job $job The Job entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(Job $job)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('job_delete', array('id' => $job->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
JobType.php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class JobType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('category', 'string')
->add('type', 'string')
->add('company', 'string')
->add('logo', 'string')
->add('url', 'string')
->add('position', 'string')
->add('location', 'string')
->add('desciption', 'text')
->add('how_to_apply', 'text')
->add('token', 'string')
->add('is_public', 'boolean')
->add('is_activated', 'boolean')
->add('email', 'string')
->add('expires_at', 'datetime')
->add('created_at', 'datetime')
->add('updated_at', 'datetime')
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Job'
));
}
/**
* Mandatory in Symfony2
* Gets the unique name of this form.
* #return string
*/
public function getName()
{
return 'add_job';
}
}
This is the error I receive
Thanks!
EDIT:
The content of app/config/services.yml
parameters:
# parameter_name: value
services:
# service_name:
# class: AppBundle\Directory\ClassName
# arguments: ["#another_service_name", "plain_value", "%parameter_name%"]
$editForm = $this->createForm(new JobType(), $job);
This is no longer possible in Symfony 3. In Symfony 3, you always have to pass the fully-qualified class name for form types:
$editForm = $this->createForm(JobType::class, $job);
Also, in your form type you're passing the type name instead of the FQCN of the type classes.
Symfony 3 has just released its first BETA, which means it's very bleeding edge. Also, there are almost zero tutorials for Symfony 3 yet (as it's so extremely bleeding edge). You're reading a Symfony 2 tutorial, so I recommend you to install Symfony 2 instead of 3.
Related
I'm saving a date with Symfony 3 and the bootstrap-datepicker.
If I fill out my form I expect it to save a date, 04/25/2017 in this case.
What I want in my datatabse is this: 04/25/2017.
Instead I get this in my dump:
2017-01-25 00:04.000000
and in my database:
2017-01-25
Dump result:
Database value:
PlayLogController:
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\PlayLog;
use AppBundle\Entity\Game;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
/**
* Playlog controller.
*
* #Route("playlog")
*/
class PlayLogController extends Controller
{
/**
* Lists all playLog entities.
*
* #Route("/", name="playlog_index")
* #Method("GET")
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$playLogs = $em->getRepository('AppBundle:PlayLog')->findAll();
return $this->render('playlog/index.html.twig', array(
'playLogs' => $playLogs,
));
}
/**
* Creates a new playLog entity.
*
* #Route("/{gameId}/new", name="playlog_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request, $gameId)
{
$playlog = new PlayLog();
$em = $this->getDoctrine()->getManager();
$game = $em ->getRepository(Game::class)->find($gameId);
$playlog->setGame($game);
$form = $this->createForm('AppBundle\Form\PlayLogType', $playlog);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/* #var $playLog PlayLog */
$playlog = $form->getData();
// echo $playlog->getGame()->getId() .'!';
$em->persist($playlog);
$em->flush();
}
return $this->render('playlog/new.html.twig', array(
'playLog' => $playlog,
'form' => $form->createView(),
));
}
/**
* Finds and displays a playLog entity.
*
* #Route("/{id}", name="playlog_show")
* #Method("GET")
*/
public function showAction(PlayLog $playLog)
{
$deleteForm = $this->createDeleteForm($playLog);
return $this->render('playlog/show.html.twig', array(
'playLog' => $playLog,
'delete_form' => $deleteForm->createView(),
));
}
/**
* Displays a form to edit an existing playLog entity.
*
* #Route("/{id}/edit", name="playlog_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, PlayLog $playLog)
{
$deleteForm = $this->createDeleteForm($playLog);
$editForm = $this->createForm('AppBundle\Form\PlayLogType', $playLog);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('playlog_edit', array('id' => $playLog->getId()));
}
return $this->render('playlog/edit.html.twig', array(
'playLog' => $playLog,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
/**
* Deletes a playLog entity.
*
* #Route("/{id}", name="playlog_delete")
* #Method("DELETE")
*/
public function deleteAction(Request $request, PlayLog $playLog)
{
$form = $this->createDeleteForm($playLog);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->remove($playLog);
$em->flush();
}
return $this->redirectToRoute('playlog_index');
}
/**
* Creates a form to delete a playLog entity.
*
* #param PlayLog $playLog The playLog entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createDeleteForm(PlayLog $playLog)
{
return $this->createFormBuilder()
->setAction($this->generateUrl('playlog_delete', array('id' => $playLog->getId())))
->setMethod('DELETE')
->getForm()
;
}
}
PlayLogType:
<?php
namespace AppBundle\Form;
use AppBundle\Entity\PlayLog;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PlayLogType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('date', DateType::class, array(
'widget' => 'single_text',
'html5' => false,
'attr' => ['class' => 'js-datepicker'],
'format' => 'mm/dd/yyyy'
)
);
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => PlayLog::class
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_playlog';
}
}
The script I use for the datepicker:
<script type="text/javascript">
$(document).ready(function () {
$('.js-datepicker').datepicker({
format: 'mm/dd/yyyy'
});
});
</script>
Format must be 'MM/dd/yyyy'. mm are minutes, which is why the time is 00:04 in your record.
The date/time format that you send to the server is going to depend on the locale that the server is in. Since I see Europe/Berlin in your screenshot above that means it's expecting the date/time to come in as d/m/Y (php format).
Alternatively the best way to eliminate all the date/time formats is to send it in either YYYY-MM-DD H:i:s format (H:i:s can be omitted if you don't care about time), or as a unix timestamp which is guaranteed to be in UTC time..
I'm following this tutorial http://symfony.com/doc/current/controller/upload_file.html but the $file isn't being converted to an UploadedFile, it stays as a string:
var_dump($file);
/home/owner/Desktop/Workspace/Documentary/src/DW/DocumentaryBundle/Controller/Admin/DocumentaryController.php:103:string '/tmp/phpmmv1LP' (length=14)
Type error: Argument 1 passed to DW\DocumentaryBundle\Uploader\PosterUploader::upload() must be an instance of Symfony\Component\HttpFoundation\File\UploadedFile, string given, called in /home/owner/Desktop/Workspace/DocumentaryWIRE/src/DW/DocumentaryBundle/Controller/Admin/DocumentaryController.php on line 106
Create Action:
/**
* #param Request $request
* #return Response
*/
public function createAction(Request $request)
{
$documentary = new Documentary();
$form = $this->createForm(DocumentaryType::class, $documentary);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$file = $documentary->getPoster();
$posterUploader = $this->getPosterUploader();
$fileName = $posterUploader->upload($file);
$documentary->setPoster($fileName);
$documentaryService = $this->getDocumentaryService();
$documentaryService->save($documentary);
}
return $this->render('DocumentaryBundle:Admin:create.html.twig', array(
'form' => $form->createView(),
));
}
View
{{ form_start(form) }}
{{ form_widget(form) }}
{{ form_end(form) }}
Form:
<?php
namespace DW\DocumentaryBundle\Form;
use DW\DocumentaryBundle\Entity\Documentary;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DocumentaryType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class)
->add('storyline', TextareaType::class)
->add('summary', TextareaType::class)
->add('year', IntegerType::class)
->add('length', IntegerType::class)
->add('status', TextType::class)
->add('views', IntegerType::class)
->add('shortUrl', TextType::class)
->add('videoId', TextType::class)
->add('videoSource', TextType::class)
->add('poster', FileType::class, [
'label' => 'Poster'])
->add('category', EntityType::class, [
'class' => 'CategoryBundle:Category',
'choice_label' => 'name',
])
->add('submit', SubmitType::class);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => Documentary::class,
));
}
/**
* #return string
*/
public function getName()
{
return "documentary";
}
}
Controller:
<?php
namespace DW\DocumentaryBundle\Uploader;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class PosterUploader
{
/**
* #var string
*/
private $targetDir;
/**
* #param string $targetDir
*/
public function __construct(string $targetDir)
{
$this->targetDir = $targetDir;
}
/**
* #param UploadedFile $file
* #return string
*/
public function upload(UploadedFile $file)
{
$fileName = md5(uniqid()).'.'.$file->guessExtension();
$file->move($this->targetDir, $fileName);
return $fileName;
}
}
The documentation is NOT wrong! They don't use type hinting in their example:
// src/AppBundle/Entity/Product.php
public function setBrochure($brochure)
{
$this->brochure = $brochure;
return $this;
}
When you use string type hinting then UploadedFile::__toString() will be invoked!
So check your entity. It should be something like this:
public function setPoster($poster)
{
// ...
}
To get a file from request, in Symfony, you need to get $request->files. For example $request->files->get('file_name');.
The documentation is wrong, it should be: $file = $form->get('poster')->getData();
/**
* #param Request $request
* #return Response
*/
public function createAction(Request $request)
{
$documentary = new Documentary();
$form = $this->createForm(DocumentaryType::class, $documentary);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$file = $form->get('poster')->getData();
$posterUploader = $this->getPosterUploader();
$fileName = $posterUploader->upload($file);
$documentary->setPoster($fileName);
$documentaryService = $this->getDocumentaryService();
$documentaryService->save($documentary);
}
return $this->render('DocumentaryBundle:Admin:create.html.twig', [
'form' => $form->createView(),
]);
}
I am using this custom form generated by console crud generator:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class SubtaskType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name')->add('description') ;
}
/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Subtask'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_subtask';
}
}
After generating the crud I added a bidirectional association between Task and Subtask, and I modified the controller accordingly. So when I add a Subtask it should always be added to the Task that is in the url. Here is how it looks like with routes in annotations:
/**
* Subtask controller.
*
* #Route("/task/{taskId}/subtask")
*/
class SubtaskController extends Controller
{
.
.
.
/**
* Creates a new subtask entity.
*
* #Route("/new", name="subtask_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request, $taskId)
{
$subtask = new Subtask();
$form = $this->createForm('AppBundle\Form\SubtaskType', $subtask);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($subtask);
$em->flush($subtask);
return $this->redirectToRoute('subtask_show', array('id' => $subtask->getId(), 'taskId' => $taskId));
}
return $this->render('subtask/new.html.twig', array(
'subtask' => $subtask,
'form' => $form->createView(),
'taskId' => $taskId
));
}
.
.
.
}
At this moment This code inserts a row in the subtask table successfully with a null value for task_id (the foreign key for task).
How can I adapt this code so that it inserts the right task_id?
Here is the work around that I found:
I added manually this method in the Subtask Entity
/**
* Set task
*
* #param Task $task
*
* #return Subtask
*/
public function setTask($task)
{
$this->task = $task;
return $this;
}
And than I modified the newAction method in this way:
/**
* Creates a new subtask entity.
*
* #Route("/new", name="subtask_new")
* #Method({"GET", "POST"})
*/
public function newAction(Request $request, $taskId)
{
$subtask = new Subtask();
$em = $this->getDoctrine()->getManager();
$task = $em->getReference('AppBundle\Entity\Task', $taskId);
$subtask->setTask($task);
$form = $this->createForm('AppBundle\Form\SubtaskType', $subtask);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($subtask);
$em->flush($subtask);
return $this->redirectToRoute('subtask_show', array('id' => $subtask->getId(), 'taskId' => $taskId));
}
return $this->render('subtask/new.html.twig', array(
'subtask' => $subtask,
'form' => $form->createView(),
'taskId' => $taskId
));
}
This gives me the expected result.
Any better way to achieve this?
I generated with the smyfony2 cli tool a CRUD for a entity. Now i want to use the edit function from the CRUD.
First, the code
/**
* Displays a form to edit an existing Poi entity.
*
* #Route("/poi/{id}/edit", name="poi_edit", requirements={"id" = "\d+"})
* #Method("GET")
* #Template()
*/
public function editAction($id){
$em = $this->getDoctrine()->getManager('default')->getRepository('Project\Bundle\ProjectBundle\Entity\Poi');
$entity = $em->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Poi entity.');
}
$editForm = $this->createEditForm($entity);
return array(
'entity' => $entity,
'edit_form' => $editForm->createView()
);
}
/**
* Creates a form to edit a Poi entity.
*
* #param Poi $entity The entity
*
* #return \Symfony\Component\Form\Form The form
*/
private function createEditForm(Poi $entity)
{
$form = $this->createForm(new PoiType(), $entity, array(
'action' => $this->generateUrl('poi_update', array('id' => $entity->getId())),
'method' => 'PUT',
));
$form->add('submit', 'submit', array('label' => 'Update'));
return $form;
}
But i receive a "Entity not found" error. Of course i first of all i thought about the throw of the NotFoundException, so i commented it out, but still i get the error.
Then i debugged the code and the entity will be found. It's also existing in the database.
I debugged further and found out that the error is thrown somewhere in the Symfony2 Form stack which generates the form, which is called in the createEditForm action.
Do i clearly miss something? What is my error?
My Symfony2 dev log also just says that a NotFoundException was thrown, no further information there to be found.
I have another entity with a generated CRUD, but there i build the form in the createEditForm by myself because of certain fields that shouldn't be displayed. Do i need to create the form by myself or am i doing something obviously wrong?
Also the PoiType code
class PoiType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('date')
->add('situation')
->add('description')
->add('isPrivate')
->add('image')
->add('audio')
->add('country')
->add('category')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\Bundle\ProjectBundle\Entity\Poi'
));
}
/**
* #return string
*/
public function getName()
{
return 'project_bundle_projectbundle_poi';
}
}
This is the Error i get:
CRITICAL - Uncaught PHP Exception Doctrine\ORM\EntityNotFoundException: "Entity was not found." at /var/www/project-symfony/vendor/doctrine/orm/lib/Doctrine/ORM/Proxy/ProxyFactory.php line 177
Try adding a backslash prefix to your data_class in your FormType as such:
'data_class' => '\Project\Bundle\ProjectBundle\Entity\Poi',
Thanks for this post.
I'm working on some simple script, but can't wrap my head around this problem.
So here it is.
/**
* Booking
* #ORM\Table()
* #ORM\Entity(repositoryClass="Tons\BookingBundle\Entity\BookingRepository")
* #UniqueEntity(fields={"room", "since", "till"}, repositoryMethod="getInterferingRoomBookings")
* #Assert\Callback(methods={"isSinceLessThanTill"}, groups={"search","default"})
*/
class Booking
and repository Method
/**
* Get room state for a certain time period
*
* #param array $criteria
* #return array
*/
public function getInterferingRoomBookings(array $criteria)
{
/** #var $room Room */
$room = $criteria['room'];
$builder = $this->getQueryForRoomsBooked($criteria);
$builder->andWhere("ira.room = :room")->setParameter("room", $room);
return $builder->getQuery()->getArrayResult();
}
The problem is that this works on create methods like it should,
but when updating existing entity - it violates this constrain.
I tried to add Id constrain, but when creating entities, id is null, so repository Method don't even starts.
Also i tried to remove Entity and then recreate it. like
$em->remove($entity);
$em->flush();
//-----------
$em->persist($entity);
$em->flush();
but this also does not work.
Create Action
/**
* Creates a new Booking entity.
*
* #Route("/create", name="booking_create")
* #Method("POST")
* #Template("TonsBookingBundle:Booking:new.html.twig")
*/
public function createAction(Request $request)
{
$entity = new Booking();
$form = $this->createForm(new BookingType(), $entity);
$form->bind($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$room = $entity->getRoom();
if($room->getLocked() && $room->getLockedBy()->getId() === $this->getUser()->getId())
{
$entity->setCreatedAt(new \DateTime());
$entity->setUpdatedAt(new \DateTime());
$entity->setManager($this->getUser());
$em->persist($entity);
$room->setLocked(false);
$room->setLockedBy(null);
$room->setLockedAt(null);
$em->persist($room);
$em->flush();
return $this->redirect($this->generateUrl('booking_show', array('id' => $entity->getId())));
}
else
{
$form->addError(new FormError("Номер в текущий момент не заблокирован или заблокирован другим менеджером"));
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
Update Action
/**
* Edits an existing Booking entity.
*
* #Route("/edit/{id}/save", name="booking_update")
* #Method("PUT")
* #Template("TonsBookingBundle:Booking:edit.html.twig")
*/
public function updateAction(Request $request, $id)
{
/** #var $em EntityManager */
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('TonsBookingBundle:Booking')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Booking entity.');
}
$editForm = $this->createForm(new BookingType(), $entity);
$editForm->bind($request);
if ($editForm->isValid()) {
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('booking_edit', array('id' => $id)));
}
return array(
'entity' => $entity,
'form' => $editForm->createView(),
);
}
I got this!
I changed annotation to this
/**
* Booking
* #ORM\Table()
* #ORM\Entity(repositoryClass="Tons\BookingBundle\Entity\BookingRepository")
* #UniqueEntity(fields={"id","room", "since", "till"}, repositoryMethod="getInterferingRoomBookings")
* #UniqueEntity(fields={"room", "since", "till"}, repositoryMethod="getInterferingRoomBookings",groups={"create"})
* #Assert\Callback(methods={"isSinceLessThanTill"}, groups={"search","default"})
*/
class Booking
Copy BookingType to BookingTypeCreate And added
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Tons\BookingBundle\Entity\Booking',
'validation_groups' => array('create'),
));
}
To form defaults. So now parameters are different when passing entity to validation method.
I think it's still a workaround method.
Short answer: You're not getting the form data into the entity, so you are working with a new entity that does not know its room has been set in the form.
Long answer: Getting the form data and putting it into the entity allows you to manipulate the data before update. See modified controller below. Key line is
$entity = form->getData(); Which then allows you to $room=$entity->getRoom();
/**
* Creates a new Booking entity.
*
* #Route("/create", name="booking_create")
* #Method("POST")
* #Template("TonsBookingBundle:Booking:new.html.twig")
*/
public function createAction(Request $request)
{
$entity = new Booking();
$form = $this->createForm(new BookingType(), $entity);
$form->bind($request);
$entity = $form->getData(); // Lighthart's addition
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
$room = $entity->getRoom();
if($room->getLocked() && $room->getLockedBy()->getId() === $this->getUser()->getId())
{
$entity->setCreatedAt(new \DateTime());
$entity->setUpdatedAt(new \DateTime());
$entity->setManager($this->getUser());
$em->persist($entity);
$room->setLocked(false);
$room->setLockedBy(null);
$room->setLockedAt(null);
$em->persist($room);
$em->flush();
return $this->redirect($this->generateUrl('booking_show', array('id' => $entity->getId())));
}
else
{
$form->addError(new FormError("Номер в текущий момент не заблокирован или заблокирован другим менеджером"));
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
}
return array(
'entity' => $entity,
'form' => $form->createView(),
);
}
Pass an additional identifier (maybe a token) to the unique check field (for example loginName) (fields={"token", "loginName"}), to the repositoryMethod. The id itself is not working, it is null on object creation and the repositoryMethod is not executed. I create the token in the constructor Method. The token is needed to identify the entity on creation or update action.
/**
* #ORM\Table(name="anbieterregistrierung")
* #ORM\Entity(repositoryClass="AnbieterRegistrierungRepository")
* #UniqueEntity(
* fields={"token", "loginName"},
* repositoryMethod="findUniqueEntity"
* )
*/
then in repository class you edit the WHERE DQL:
public function findUniqueEntity(array $parameter)
{
$loginName = $parameter['loginName'];
$token = $parameter['token'];
.
.
.
. . . WHERE anbieterregistrierung.loginName = :loginName AND anbieterregistrierung.token <> :token
.
.
.
}