ReferenceMany in Doctorine with Symfony - php

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;
}

Related

Symfony 3 doctrine show content from relationship on repo

I have two Symfony entities SrvrsServers and NcBackupEvents:
NCbrtBundle:SrvrsServers (id, name, description, etc)
Relationship code:
/**
* One SrvrsServers has Many NcBackupEvents.
* #ORM\OneToMany(targetEntity="NcBackupEvents", mappedBy="SrvrsServers")
*/
private $ncBackupEvents;
public function __construct() {
$this->ncBackupEvents = new ArrayCollection();
}
NCbrtBundle:NcBackupEvents (id, date, status, etc)
This is on NcBackupEvents:
/**
* #var \SrvrsServers
*
* #ORM\ManyToOne(targetEntity="SrvrsServers")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="srvrs_servers_id", referencedColumnName="id")
* })
*/
private $srvrsServers;
The relationship is OneToMany from SrvrsServers to NcBackupEvents. On the repo of NcBackupEvents i'm able to pull information about itself. This works and I think the SrvrsServers object gets pulled, but I am not 100% sure and also do not know how to access it:
class NcBackupEventsRepository extends \Doctrine\ORM\EntityRepository
{
public function findByServerBackup($parameters)
{
$dql = 'SELECT n FROM NCbrtBundle:NcBackupEvents n
WHERE n.backupmethod LIKE :backupmethod
AND n.backupType LIKE :backup_type
AND n.log LIKE :log';
$query = $this->getEntityManager()
->createQuery($dql)
->setParameter('backupmethod',
'%'.$parameters['backupmethod'].'%')
->setParameter('backup_type',
'%'.$parameters['backup_type'].'%')
->setParameter('log', '%'.$parameters['log'].'%');
try {
return $query->getResult();
} catch (\Doctrine\ORM\NoResultException $e) {
return null;
}
}
}
How can I QUERY from Controller using the SrvrsServers->name field for instance? I have tried many things and nothing seems to work. This should be very simple, but I do not understand how to get it done. I have tried to follow the official docs and can not get it to work for some reason. Can someone point me the way to do this? Please keep present that I am a newbie.
EDIT: SAMPLE Controller.
/**
* #Route("/")
*/
public function indexAction()
{
$paramaters = array('backupmethod' => 't-py',
'backup_type' => '', 'log' => 'fuc');
$em = $this->getDoctrine()
->getRepository('NCbrtBundle:NcBackupEvents')
->findByServerBackup($paramaters);
foreach($em as $value){
print_r(gettype($value));
print_r($value->getSrvrsServers()->getName());
}
return $this->render('NCbrtBundle:Default:index.html.twig');
}
EDIT2: I want to include the server name on the $paramaters variable from the SrvrsServers entity

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'));

Saving from eloquent constructor in laravel

So, I'm currently working on a browser game in Laravel. So far I love the framework, but I haven't really got much experience, and I just can't get this to work.
Basically I'm trying to update all users whenever they are instantieted, as there is no reason update them when they are not used. But calling this function from the constructor doesn't update the user, it only works when I call the function outside the constructor.
Have I missed anything, or is it just not possible?
Thanks in advance!
<?php
class User extends Eloquent implements UserInterface, RemindableInterface {
use UserTrait, RemindableTrait;
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password', 'remember_token');
public function __construct($arguments = array())
{
parent::__construct($arguments);
$this->updateHp();
}
public function updateHp()
{
$this->hp_last = time();
$this->save();
}
}
Eloquent is a static class, data is fetched on query (find, first, get) and when you create a model you have just a blank model, with no data on it. This is, as example, the point where you have some data available:
public static function find($id, $columns = array('*'))
{
if (is_array($id) && empty($id)) return new Collection;
$instance = new static;
return $instance->newQuery()->find($id, $columns);
}
Before one of those query methods, you have void.
So you probably cannot do that during __construct because your model is still blank (all nulls). This is what you can do to make it, somehow, automatic:
First, during boot, create some creating and updating listeners:
public static function boot()
{
static::creating(function($user)
{
$user->updateHp($user);
});
static::updating(function($user)
{
$user->updateHp($user);
});
parent::boot();
}
public function updateHp()
{
$this->hp_last = time();
$this->save();
}
Then, every time you save() a model it will, before saving, fire your method:
$user = User::where('email', 'acr#antoniocarlosribeiro.com')->first();
$user->activation_code = Uuid::uuid4();
$user->save();
If you want to make it somehow automatic for all your users. You can hook it to a login event. Add this code to your global.php file:
Event::listen('user.logged.in', function($user)
{
$user->updateHp();
})
Then in your login method you'll have to:
if ($user = Auth::attempt($credentials))
{
Event::fire('user.logged.in', array($user));
}
In my opinion you shouldn't do that. If you use the code:
$user = new User();
you would like to be run:
$this->hp_last = time();
$this->save();
and what exactly should happen in this case? New user without id should be created with property hp_last ?
I think that's not the best idea.
You should leave it in the function then you can use:
$user = new User();
$user->find(1);
$user->updateHp();
That makes much more sense for me.

Symfony2 set value for formfield in controller

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?)

Categories