Symfony2 set value for formfield in controller - php

I'm getting following exception, when I try to add a task and automaticly add the current user to the accordion field of the task:
Catchable Fatal Error: Argument 1 passed to Seotool\MainBundle\Entity\Task::setUser() must be an instance of Seotool\MainBundle\Entity\User, string given, called in /Applications/MAMP/htdocs/Seotool/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 438 and defined in /Applications/MAMP/htdocs/Seotool/src/Seotool/MainBundle/Entity/Task.php line 174
My Controller looks like this:
/**
#Route(
* path = "/taskmanager/user/{user_id}",
* name = "taskmanager"
* )
* #Template()
*/
public function taskManagerAction($user_id, Request $request)
{
/* #### NEW TASK FORM #### */
$task = new Task();
$addTaskForm = $this->createForm(new TaskType(), $task);
$addTaskForm->handleRequest($request);
if($addTaskForm->isValid()):
$task->setDone(FALSE);
$task->setUser($user_id);
$task->setDateCreated(new \DateTime());
$task->setDateDone(NULL);
$em = $this->getDoctrine()->getManager();
$em->persist($task);
$em->flush();
return $this->redirect($this->generateUrl('taskmanager', array('user_id' => $user_id)));
endif;
Line 174 in Entity/Task.php:
/**
* Set User
*
* #param \Seotool\MainBundle\Entity\User $user
* #return Task
*/
public function setUser(\Seotool\MainBundle\Entity\User $user = null)
{
$this->User = $user;
return $this;
}
Does anybody know how to set the value for my hidden "user" form field with $user_id value?
Thanks in advance

You can't do
$task->setUser($user_id);
because $user_id, here, is a string and your method signature expects an object of Seotool\MainBundle\Entity\User type.
You can proceed in two ways, depending on your needs (but I suppose that only second option will be suitable for you):
1) Modify your setUser() function if you don't need a reference (ORM oriented) or User Object into your Task Object
public function setUser($user = null)
{
$this->User = $user;
return $this;
}
2) Retrieve current user and set it
public function taskManagerAction($user_id, Request $request)
{
[...]
if($addTaskForm->isValid()):
$user = $this->get('security.context')->getToken()->getUser();
$task->setUser($user);
[...]
endif;
$this->get('security.context')->getToken()->getUser(); gives you current logged user but, maybe, you need to change signature aswell (possible inheritance issues?)

Related

How solve instance problem with Doctrine?

I'm new learner of Symfony 4 and I'm looking for help. I've an Entity named "Player" and I want to generate a random confirmation number.
For now, I'm using a variable $confirmNbr and I save the $confirm in my database with $participant->setConfirmationNumber($confirmNbr);.
What I want it's create a function generateRandomNumber() in my Entity Player.php like this :
public function generateConfirmationNumber() : self
{
$this->confirmationNumber = substr(str_shuffle(str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',6)),0,5);
return $this;
}
This is my Controller file
/**
* #Route("/", name="homepage")
*/
public function new(Player $player, EntityManagerInterface $em, Request $request)
{
$participant = $this->playerrepo->findAll();
$form = $this->createForm(PlayerFormType::class);
$randomNbr = $player->generateConfirmationNumber();
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$participant = new Player;
$participant->setName($data['name']);
$participant->setFirstname($data['firstname']);
$participant->setEmail($data['email']);
$participant->setConfirmationNumber($confirmNbr);
$participant->setRegisterAt(new \DateTime);
$em->persist($player);
$em->flush();
$this->addFlash('success', 'Player added!');
return $this->redirectToRoute('homepage');
}
return $this->render('app/subscribe.html.twig', [
'playerForm' => $form->createView(),
'player'=>$player,
]);
}
And this is my error message :
Unable to guess how to get a Doctrine instance from the request
information for parameter "player".
Can you help me please ?
Your method is expecting an instance of the Player object - where should it come from? Doctrine is trying to guess it and get it from the URL, but it cannot. Your method is for creating new players - why do you need an instance of a player? Just remove that parameter from the method signature, i.e. change it to:
public function new(EntityManagerInterface $em, Request $request)
I've found the solution. I've modified my set function and deleted my function that I've added. Everything works!

"Call to a member function updateJob() on null"

Now, I know this question has been asked a lot, but I searched and searched but I just can't figure it out and I've been stuck for hours now. I'm really sorry if it turnes out to be a really dumb mistake (It probably will).
So, I have my Controller which instatiates the editAction() function when a button on my website is pressed. It checks if the request is a _POST request, passes on the data and checks if the input given is valid, all this works fine.
I then try to access a function in my Manager class. And that's where the error is happening and my website spits out:
"Call to a member function updateJob() on null".
Now, PhpStorm is not saying there's an error or a warning, it recognizes the jobManager class and I've checked the namespaces and class names, but all are correct. The variables are also defined correctly, as far as I can see. I'd be really thankful if someone could tell me what I am doing wrong. The code is below.
current state after adding $jobManager to __construct:
class IndexController extends AbstractActionController
{
/**
* Entity manager.
* #var EntityManager
*/
private $entityManager;
/**
* Post manager.
* #var JobManager
*/
private $jobManager;
public function __construct($entityManager, $jobManager)
{
$this->entityManager = $entityManager;
/***
* Edit from comment advice:
* I have added this line to my __construct
* But this does not solve the issue.
***/
$this->jobManager = $jobManager;
}
public function indexAction()
{
// Get recent jobs
$jobs = $this->entityManager->getRepository(Jobs::class)
->findBy(['status'=>Jobs::STATUS_READY]
);
// Render the view template
return new ViewModel([
'jobs' => $jobs
]);
}
public function editAction()
{
// Create the form.
$form = new JobForm();
// Get post ID.
$jobId = $this->params()->fromRoute('id', -1);
// Find existing job in the database.
$jobs = $this->entityManager->getRepository(Jobs::class)
->findOneById($jobId);
if ($jobs == null) {
$this->getResponse()->setStatusCode(404);
return;
}
// Check whether this job is a POST request.
if ($this->getRequest()->isPost()) {
// Get POST data.
$data = $this->params()->fromPost();
// Fill form with data.
$form->setData($data);
if ($form->isValid()) {
// Get validated form data.
$data = $form->getData();
// Use job manager service to add new post to database.
$this->jobManager->updateJob( $jobs, $data);
// Redirect the user to "backups" page.
return $this->redirect()->toRoute('backups');
}
} else {
$data = [
'id' => $jobs->getId(),
'jobName' => $jobs->getJobName(),
'status' => $jobs->getStatus(),
'vmId' => $jobs->getVmId(),
'targetfolderPrefix' => $jobs->getTargetFolderPrefix(),
'numberOfBackups' => $jobs->getNumberOfBackups(),
'lastBackupUsed' => $jobs->getLastBackupUsed(),
'repeat' => $jobs->getRepeat(),
'scheduleRunAtMinute' => $jobs->getScheduleRunAtMinute(),
'scheduleRunAtHour' => $jobs->getScheduleRunAtHour(),
'scheduleRunAtDOW' => $jobs->getScheduleRunAtDOW(),
'hostId' => $jobs->getHostId()
];
$form->setData($data);
}
// Render the view template.
return new ViewModel([
'form' => $form,
'jobs' => $jobs
]);
}
}
What is wrong
$this->jobManager->updateJob( $jobs, $data);
You are telling PHP:
"In $this class, look in the jobManager object and run the method updateJob with these variables.... "
But in $this class you have written:
/**
* Post manager.
* #var JobManager
*/
private $jobManager;
But you have nowhere set jobManager to be anything. You have no setter function in the class as well as no other function setting what a jobManager variable actually is... so jobManager can never be anything.
So what you're in effect doing is saying to PHP
"In $this class, look in the jobManager empty null-space and run the method updateJob with these variables..."
This is clearly not going to end well.
How to fix it
You need to set what jobManager is before you can use it, as referenced by Xatenev. Typically when you construct the class or using a Setter method, if preferred.
ONE:
public function __construct(EntityManager $entityManager, JobManager $jobManagerVar)
{
$this->entityManager = $entityManager;
$this->jobManager = $jobManagerVar;
}
Alternatively - if ->jobManager method needs to be defined after the object IndexController is created; then you need to use a Setter class (because the jobManager var is *private*.
Thus TWO:
class IndexController extends AbstractActionController
{
...
public function setJobManager($jobManagerVar){
$this->jobManager = $jobManagerVar
}
...
}
And then when you instantiate the IndexController you can do:
// ONE from above:
$theClass = new IndexController($entity,$jobManager);
or
// TWO from above
$theClass = new IndexController($entity);
...
$theClass->setJobManager($jobManger);
There are various other nuances as to methods of setting values in classes, I'm not going to go over them all, it will depend on what's going on in your wider project.

ReferenceMany in Doctorine with Symfony

I am using Symfony with Doctrine.
The annotation for $members (getMembers() returns this variable):
/**
* #var User
* #MongoDB\ReferenceMany(targetDocument="something", storeAs="dbRef")
* #Assert\NotNull
* #JMS\Groups({"Default", "something"})
*/
protected $members;
The controller: (I used $form->submit)
public function updateAction($id, Request $request)
{
$project = $this->fetchProject($id);
$oldMembers = $project->getMembers();
$form = $this->createForm(...);
$form->submit($request->request->all(), false);
$newMembers = $project->getMembers();
...
$this->persist(...);
I add new members in the form and submit it but the '$oldMembers' and the '$newMembers' are the same! which is not desirable!
both of them are referring to the the new data (getMembers()). but I need to keep $oldMembers separate from $newMembers. how?
try to refresh the entity manager like this:
$em = $this->getDoctrine();
$em->refresh($project);
Or you can add member manually if refresh doesn't work
You are using a Form, but if you can add manually members like this:
$project->addMember($member);
Into your entity you can have a method like this:
public function addMember(Member $member)
{
$this->member[] = $member;
return $this;
}

Symfony2: Prevent duplicate in database with form Many to One

I have a Parents form embedded into another form Student containing the data of the parents of a student with an association of Many to one.
When a new student registration are recorded his parents in another table in the database. Then if a new student who is brother of an existing need to register, meaning that parents are already registered in the database, should be prevented from parents to register again in the database, could only upgrade .
I'm told that this is solved using data transformers, but I do not know how to use it. If someone could help me I would appreciate it. Here I leave the code:
StudentType.php
//...
->add('responsible1', new ParentsType(),array('label' => 'Mother'))
->add('responsible2', new ParentsType(),array('label'=> 'Father'))
Entity Parents
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
//National identity document
//we have removed "#UniqueEntity(fields={"NID"}, message="...")"
//so you can put any NID on the form and then check its existence to insert or not.
/**
* #var string
*
* #ORM\Column(name="NID", type="string", length=10)
* #Assert\NotBlank()
*/
private $nid;
//more properties...
/**
* #ORM\OneToMany(targetEntity="Student", mappedBy="$responsible1")
* #ORM\OneToMany(targetEntity="Student", mappedBy="$responsible2")
*/
private $students;
//...
public function addStudent(\Cole\BackendBundle\Entity\Student $students)
{
$this->students[] = $students;
return $this;
}
public function removeStudent(\Cole\BackendBundle\Entity\Student $students)
{
$this->students->removeElement($students);
}
public function getStudents()
{
return $this->students;
}
Entity Student
//...
/**
* #ORM\ManyToOne(targetEntity="Parents", inversedBy="students", cascade={"persist"})
*/
private $responsible1;
/**
* #ORM\ManyToOne(targetEntity="Parents", inversedBy="students", cascade={"persist"})
*/
private $responsible2;
//...
public function setResponsible1($responsible1)
{
$this->responsible1 = $responsible1;
return $this;
}
public function getResponsible1()
{
return $this->responsible1;
}
public function setResponsible2($responsible2)
{
$this->responsible2 = $responsible2;
return $this;
}
public function getResponsible2()
{
return $this->responsible2;
}
ParentsRepository.php
class ParentsRepository extends EntityRepository
{
public function findResponsible($nid)
{
return $this->getEntityManager()->createQuery(
'SELECT p FROM BackendBundle:Parents p WHERE p.nid=:nid')
->setParameter('nid',$nid)
->setMaxResults(1)
->getOneOrNullResult();
}
}
StudentController.php
/**
* Creates a new Student entity.
*
*/
public function createAction(Request $request)
{
$entity = new Student();
$form = $this->createCreateForm($entity);
$form->handleRequest($request);
if ($form->isValid()) {
$responsible1 = $em->getRepository('BackendBundle:Parents')->findResponsible($entity->getResponsible1()->getNid());
$responsible2 = $em->getRepository('BackendBundle:Parents')->findResponsible($entity->getResponsible2()->getNid());
if($responsible1){
$entity->setResponsible1($responsible1->getId());
}
if($responsible2){
$entity->setResponsible2($responsible2->getId());
}
$entity->getResponsible1()->setUsername($entity->getResponsible1()->getNid());
$entity->getResponsible2()->setUsername($entity->getResponsible2()->getNid());
$entity->getResponsible1()->setPassword($entity->getResponsible1()->getNid());
$entity->getResponsible2()->setPassword($entity->getResponsible2()->getNid());
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('student_show', array('id' => $entity->getId())));
}
return $this->render('BackendBundle:Student:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
With the above code attempts to solve the problem but it gives me error to persist data to the database and will not let me add to the database, but if you use the following code to test the new student creates and assigns parents corresponding not create them again (assuming you were already created earlier).
$responsible1 = $em->getRepository('BackendBundle:Parents')->findResponsible(4); //The number corresponds to the id of the parent
$responsible2 = $em->getRepository('BackendBundle:Parents')->findResponsible(5);
$entity->setResponsible1($responsible1->getId());
$entity->setResponsible2($responsible2->getId());
I do not know if what I'm doing is right.I read something to use Data Transformers or event listener as PrePersist and Preupdate, but I don't know how to use this.
Thanks in advance for your answers.
Instead of
if($responsible1){
$entity->setResponsible1($responsible1->getId());
}
if($responsible2){
$entity->setResponsible2($responsible2->getId());
}
$entity->getResponsible1()->setUsername($entity->getResponsible1()->getNid());
$entity->getResponsible2()->setUsername($entity->getResponsible2()->getNid());
$entity->getResponsible1()->setPassword($entity->getResponsible1()->getNid());
$entity->getResponsible2()->setPassword($entity->getResponsible2()->getNid());
you can write
if($responsible1){
$entity->setResponsible1($responsible1);
}
if($responsible2){
$entity->setResponsible2($responsible2);
}
And it should work.
But I think a better solution will be to add an event listener to the FormEvents::SUBMIT event. This event allows you to change data from the normalized representation of the form data. So all you need to do is something like this:
public function onSubmit(FormEvent $event)
{
$student = $event->getData();
if ($student->getResponsible1()) {
$parentNid = $student->getResponsible1()->getNid();
// here you check the database to see if you have a parent with this nid
// if a parent exists, replace the current submitted parent data with the parent entity existing in your db
}
Hope this helps. Let me know if I have to give more details.
Judging from your relationship, you want to avoid that the same student is added twice to the Parents entity. There is a simple trick for that, ArrayCollaction class has a method named contains it returns true if a value or object is already found in the collection. A better in_array.
So, you need to check inside the adder if the $parent already contains the $student that is about to be added and act accordingly. Like shown below:
public function addStudent(\Cole\BackendBundle\Entity\Student $student)
{
if (!$this->students->contains($student)) {
$this->students[] = $students;
}
return $this;
}
Here's my thoughts, from the comments, you said you are using a national identity document(hopefull its an integer representation), make this the primary key of the parent table and make this unique, so when the second student who is the sibling of a another student enters the same details and submits, the database will throw an error, handle that error and continue on
edit: it may not even be required to make the national identity the primary key, just make it unique, you were supposed to do this regardless, you missed this one.
you can use symfony entity form type to load (ajax) the parent entity when the student enters the national identity

Doctrine preUpdate does not actually change entity

I am using Doctrine 2.4.6 in my Symfony 2.6.1 project. The problem is that changes made to entity in preUpdate callback are not saved in database. Code follows:
class MyListener {
public function preUpdate(PreUpdateEventArgs $args) {
$entity = $args->getEntity();
$args->setNewValue('name', 'test');
// echo $args->getNewValue('name'); --> prints 'test'
}
}
class DefaultController extends Controller {
/**
* #Route("/commit", name="commit")
*/
public function commitAction(Request $request) {
$content = $request->getContent();
$serializer = $this->get('jms_serializer');
/* #var $serializer \JMS\Serializer\Serializer */
$em = $this->getDoctrine()->getManager();
/* #var $em \Doctrine\Orm\EntityManagerInterface */
$persons = $serializer->deserialize($content, 'ArrayCollection<AppBundle\Entity\Person>', 'json');
/* #var $persons \AppBundle\Entity\Person[] */
foreach($persons as $person) {
$em->merge($person);
}
$em->flush();
return new JsonResponse($serializer->serialize($persons, 'json'));
// Person name is NOT 'test' here.
}
}
The preUpdate doesn't allow you to make changes to your entities. You can only use the computed change-set passed to the event to modify primitive field values. I bet if you check the database you'll see that the Person entities did get updated, you just won't see them in the $persons variable until the next time you manually retrieve them.
What you'll have to do after the flush is retrieve the entities from the database to see their updates values:
$em->flush();
$personIds = array_map(function($person) { return $person->getId(); }, $persons);
$updatedPersons = $em->getRepository('AppBundle:Person')->findById($personIds);
return new JsonResponse($serializer->serialize($updatedPersons, 'json'));

Categories