Flush One-To-Many, Self-referencing - php

I have self-referencing one-to-many relationship (I did it in accordance with Documentation: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-self-referencing )
Now I want flush new element to Entity, but how can I use ID of new flush element to put it in referencing place?
I try with:
$newProject->setSubprojectId($newProject);
and with:
$newProject->setSubprojectId($newProject->getId());
but I think that it's impossible to get ID of object before flush it. I'm right?
My code looks like:
/**
* #Route("/project/manage", name="project_edit");
*/
public function registerAction(Request $request)
{
$user = $this->get('security.token_storage')->getToken()->getUser();
$newProject = new Project();
$newProject->setIsActive(1);
$newProject->setOwner($user);
$newProject->setCreateDate( new \DateTime() );
$newProject->setSubprojectId($newProject);
$formProject = $this->createForm(AddProject::class, $newProject);
$formProject->handleRequest($request);
if($formProject->isSubmitted() && $formProject->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($newProject);
$em->flush();
return $this->render('xxx/projectEdit.html.twig', array(
'form' => $formProject->createView(),
));
}
return $this->render('xxx/projectEdit.html.twig', array(
'form' => $formProject->createView(),
));
}
EDIT
I found different solutions but the question remains valid - it's possible to get this id before flush or to flush id for two columns in the same time?

You could try setting cascade persist, however why not just do:
$em = $this->getDoctrine()->getManager();
$em->persist($newProject);
$em->flush();
$newProject->setSubProject($newProject); // *why* do you want to do this?
$em->persist($newProject);
$em->flush();
You shouldn't need to mess about with setting ids directly. You should be able to accomplish this by using the setter on your object and doctrine should take care of the ids for you.

Related

Symfony2 get new value after UPDATE query

in my Symfony2.8 app I got the following controller:
public function changetariffAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$user = $this->container->get('security.context')->getToken()->getUser();
$userid = $user->getId();
$tariff = $user->getTariff();//tariff1 here
$paymentForm = $this->createPaymentForm($user);
$paymentForm->handleRequest($request);
if($tariff != 'tariff2') {
$query = $em->createQuery('UPDATE My\UserBundle\Entity\User u SET u.tariff = :tariff2 WHERE u.id = :userid');
$query->setParameter('userid', $user->getId());
$query->setParameter('tariff2', 'tariff2');
$query = $query->getResult();//returns 1 here, tariff field in DB is set to tariff2 as expected
$query = $em->createQuery('SELECT u FROM My\UserBundle\Entity\User u WHERE u.id = :userid');//getting once again user entity but it did not change
$query->setParameter('userid', $user->getId());
$user = $query->getResult();
$tariff_upd = $user[0]->getTariff();//tariff1 here but I need tariff2!
//Also I tried to persist and flush user entity here but it did not work
return $this->render('MyBundle:Pages:tariffchangesuccess.html.twig', array(
'user' => $user,
'form' => $paymentForm->createView(),
'tariff' => $tariff_upd //still tariff1 but I need tariff2
));
}
return $this->render('MyBundle:Pages:tariffchangesuccess.html.twig', array(
'user' => $user,
'form' => $paymentForm->createView(),
'tariff' => $tariff
));
}
My Controller works ok and all the values are updated in my DB as expected but new values (tariff2) are not rendered in my twig template. New values are rendered only when I update the page in my browser (hit F5), but this is not an expected behavior. Any ideas how to fix that? Thank you.
Doctrine use something similar as cache and maybe your use of queries instead of natives methods short-circuit this system. Docrtine can handle your entities and know what to record and has been changed etc. But you have to use Doctrine functions or repositories for that, and not do it througt custom queries... The Doctrine way should be something like:
$em = $this->getDoctrine()->getManager();
$userid = $this->container->get('security.context')->getToken()->getUser()->getId()
// Get object user from DB values
$user = $em->getRepository('My\UserBundle:User')->findOneById($userid );
// Update tarif in the user object
$user->setTariff('tariff2');
// Let Doctrine write in the DB. The persist() may not be necesary as Doctrine already manage this object, but it's a good practise.
$em->persist($user);
$em-> flush();
// Doctrine should already have update the $user object, but if you really really want to be sure, you can reload it:
$user = $em->getRepository('My\UserBundle:User')->findOneById($userid );
You can use the refresh method of the EntityManager in order to:
Refreshes the persistent state of an entity from the database,
overriding any local changes that have not yet been persisted.
So add the refresh call, as example:
$query = $em->createQuery('SELECT u FROM My\UserBundle\Entity\User u WHERE u.id = :userid');//getting once again user entity but it did not change
$query->setParameter('userid', $user->getId());
$user = $query->getResult();
// Force refresh of the object:
$em->refresh($user);
$tariff_upd = $user[0]->getTariff();//tariff1 here but I need tariff2!
Hope this help

phalcon 2.0.13 set data with magic setter to related model

I have a problem with phalcon model magic getter and setter.
I want to update like this tutorial :
https://docs.phalconphp.com/en/latest/reference/models.html#storing-related-records
But the thing is my proj is multi module and separated models folder.
So I have to use alias for hasOne and belongsTo
$this->hasOne('user_id', '\Models\UserProfile', 'user_id', array('alias' => 'UserProfile'));
and
$this->belongsTo('user_id', '\Models\CoreUser', 'user_id', array('alias' => 'CoreUser'));
What i want to do is like this.
$CoreUser = new CoreUser();
$user = $CoreUser->findFirst(array(
//...condition here to find the row i want to update
));
$user->assign($newUserData);
$user->setUserProfile($newProfileData);
$user->update();
But above this code only save user data and don't save Profile data at all. (have profile data -- confirmed)
So do you have any idea what the error is? if u know, Please help me or give me a tip.
I got it now.. when assigning like $user->UserProfile = $newUserProfile;
$newUserProfile should b a Model Object.
So my new code is
$CoreUser = new CoreUser();
$user = $CoreUser->findFirst(array(
//...condition here to find the row i want to update
));
$profile = $user->UserProfile; //$profile is now model object which related to $user
//assign new array data
$profile->assign($newProfileData);
$user->assign($newUserData);
/*
* can also assign one by one like
* $user->first_name = $newProfileData['first_name'];
* but cannot be like $profile = $newProfileData or $user->UserProfile = $newProfile
* since it's gonna override it the model with array
*/
$user->UserProfile = $profile;
$user->update(); // it's working now
Thanks to #Timothy for the tips too .. :)
Instead of doing
$profile = $user->UserProfile;
You should instantiate a new UserProfile object
// find your existing user and assign updated data
$user = CoreUser::findFirst(array('your-conditions'));
$user->assign($newUserData);
// instantiate a new profile and assign its data
$profile = new UserProfile();
$profile->assign($newProfileData);
// assign profile object to your user
$user->UserProfile = $profile;
// update and create your two objects
$user->save();
Note that this will always create a new UserProfile. If you want to use the same code to update and create a UserProfile, you can maybe do something like:
// ...
// instantiate a (new) profile and assign its data
$profile = UserProfile::findFirstByUserId($user->getUserId());
if (!$profile) {
$profile = new UserProfile();
}
$profile->assign($newProfileData);
// ...

Calling an entity's setter for updating its column from a controller of another entity

I need to update the Client table's budget column after inserting a new Budget into the database, but it doesn't. This is how I am doing inside of BudgetController::addAction():
if ($form->isValid()) {
$manager->persist($form->getData());
$manager->flush();
$Client = $manager->getReference('PanelBundle:Client', $form['client_id']->getData()->getId());
$Client->setBudget($manager->getRepository('PanelBundle:Budget')->getLastId());
$this->addFlash('success', 'Novo orçamento adicionado');
return $this->redirect($this->generateUrl('panel_budgets'));
}
The $Client declaration returns the Client name successfully, but the line where it sets the setBudget() seem not to work. I don't know how to make an update like this. I need it to update after inserting into Budget according to the selected Client id in Budget form.
Client and Budget are related to oneToMany and manyToOne, respectively, am I missing something because of this relationship?
If the Budget entity is a ManyToOne association of the Client, then you should be using ->addBudget() instead of a setter. It's also probably better to do a ->find() for the Client entity instead of a ->getReference(). If you really want to save the extra trip to the database, use the setter on the Budget entity instead to set the $client proxy created by the ->getReference(), i.e. $budget->setClient($client);. But it's not that expensive to find the Client and it ensures that the Client of that id exists. It would then also be a good idea to flush the manager again, just to make sure things are wrapped up cleanly, instead of assuming it will all happen without interruption as the kernel terminates. A complete rendition of your controller and action should look something like this:
namespace PanelBundle\Controller;
use PanelBundle\Entity\Budget;
use PanelBundle\Form\Type\BudgetType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class BudgetController extends Controller
{
public function addAction(Request $request)
{
$budget = new Budget();
$budgetForm = $this->createForm(new BudgetType(), $budget);
$budgetForm->handleRequest($request);
if ($budgetForm->isValid()) {
$manager = $this->getDoctrine()->getManager();
$manager->persist($budget);
$manager->flush();
$client = $manager->getRepository('PanelBundle:Client')
->find($budgetForm->get('client_id')->getData())
;
$client->addBudget($budget);
$manager->flush();
$this->addFlash('success', 'Novo orçamento adicionado');
return $this->redirect($this->generateUrl('panel_budgets'));
}
return $this->render(
'PanelBundle:Budget:add.html.twig',
array(
'budgetForm' => $budgetForm->createView(),
)
);
}
}

Save elements when using the session Symfony

I am trying to use the session with Symfony. In fact, I want to complete a form before login.
For this reason, I would like this scheme:
Save the different fields in the session
Login/register
Save in the database
My controller:
public function customMadeAction(Request $request)
{
$session = $this->container->get('session');
$user = $this->container->get('security.context')->getToken()->getUser();
$CustomMade = new CustomMade();
$form = $this->createForm(new CustomMadeType(), $CustomMade);
$form->handleRequest($request);
if ($user === 'anon.') {
if($form->isValid()) {
# 1-Save in session
var_dump($CustomMade); #I have an array with all I need
$session->set('infos', $CustomMade); #It does not save my informations
# 2-Redirect to login/register
$securityContext = $this->container->get('security.context');
if (!$securityContext->isGranted('ROLE_USER')) {
throw new AccessDeniedException('Accès refusé');
} else {
# 3-Save in database
$em = $this->getDoctrine()->getManager();
$em->persist($CustomMade);
$em->flush();
}
}
}else{
if($form->isValid()) {
$CustomMade->setIdUser($user->getId());
$em = $this->getDoctrine()->getManager();
$em->persist($CustomMade);
$em->flush();
}
}
return $this->render('FrontBundle:Forms:customMade.html.twig', array(
'form' => $form->createView()
));
}
I do not think you can store an object in a session.
Here are a few solutions :
store manually each field of your object in the session
serialize your object and save it in the session
store it in the database with a flag saying it is not yet attached to a user, and save the id in session
As I see it, 1 is the fastest to implement, but it will be a pain to maintain if you later add fields to your entity. Solution 2 is better, but the deserialization is not so easy. I think solution 3 is the best, it is not that complicated to implement and will not cause any maintainance issue.
If you want to save the data, save only the input data from the form (after validation) and not the entirely entity (with possibly a lot of... shit, unless that is what you want).
You can just set the object/array into the session and Symfony will take care of serializing and unserializing it for you
If this is the case, replace
$session->set('infos', $CustomMade);
by
$session->set('infos', $form->getData());
This way you'll be later simply do:
$data = $session->get('infos');
The solution of Antoine is right,
You will have to serialize the object in order to be able to store it in the session.
The easiest way should be using the JMSSerializerBundle and decode your object as json:
$serializer = $this->container->get('serializer');
$jsonDecoded = $serializer->serialize($doctrineobject, 'json');
Btw. as stated in the comments this will serialize the whole doctrine structure connected to the entity.
However a toArray() function could be implemented into the entity in order to serialize an array structure only, but this approach becomes very complex when dealing with oneToOne oneToMany and manyToMany relationships

Delete records in Doctrine

I'm trying to delete a record in Doctrine, but I don't know why it's not deleting.
Here is my Code:
function del_user($id)
{
$single_user = $entityManager->find('Users', $id);
$entityManager->remove($single_user);
$entityManager->flush();
}
Plus: How can I echo query to see what going on here?
This is an old question and doesn't seem to have an answer yet. For reference I am leaving that here for more reference. Also you can check the doctrine documentation
To delete a record, you need to ( assuming you are in your controller ):
// get EntityManager
$em = $this->getDoctrine()->getManager();
// Get a reference to the entity ( will not generate a query )
$user = $em->getReference('ProjectBundle:User', $id);
// OR you can get the entity itself ( will generate a query )
// $user = $em->getRepository('ProjectBundle:User')->find($id);
// Remove it and flush
$em->remove($user);
$em->flush();
Using the first method of getting a reference is usually better if you just want to delete the entity without checking first whether it exists or not, because it will not query the DB and will only create a proxy object that you can use to delete your entity.
If you want to make sure that this ID corresponds to a valid entity first, then the second method is better because it will query the DB for your entity before trying to delete it.
For my understanding if you need to delete a record in doctrine that have a doctrine relationship eg. OneToMany, ManyToMany and association cannot be easy deleted until you set the field that reference to another relation equal to null.
......
you can use this for non relation doctrine
$entityManager=$this->getDoctrine()->getManager();
$single_user=$this->getDoctrine()->getRepository(User::class)->findOneBy(['id'=>$id]);
$entityManager->remove($single_user);
$entityManager->flush();
but for relation doctrine set the field that reference to another relation to null
$entityManager=$this->getDoctrine()->getManager();
$single_user=$this->getDoctrine()->getRepository(User::class)->findOneBy(['id'=>$id]);
{# assume you have field that reference #}
$single_user->setFieldData(null);
$entityManager->remove($single_user);
$entityManager->flush();
do you check your entity as the good comment annotation ?
cascade={"persist", "remove"}, orphanRemoval=true
In a Silex route I do like this, in case it helps someone:
$app->get('/db/order/delete', function (Request $request) use ($app) {
...
$id = $request->query->get('id');
$em = $app['orm.em']; //or wherever your EntityManager is
$order = $em->find("\App\Entity\Orders",$id); //your Entity
if($order){
try{
$em->remove($order);
$em->flush();
}
catch( Exception $e )
{
return new Response( $e->getMessage(), 500 );
}
return new Response( "Success deleting order " . $order->getId(), 200 );
}else{
return new Response("Order Not Found", 500);
}
}
You first need repository.
$entityManager->getRepository('Users')->find($id);
instead of
$single_user = $entityManager->find('Users', $id);
'Users' String is the name of the Users repository in doctrine ( depends if you are using Symfony , Zend . . etc ).
First, You may need to check if 'Users' is your fully qualified class name. If not check, and update it to your class name with the namespace info.
Make sure the object returned by find() is not null or not false and is an instance of your entity class before calling EM's remove().
Regarding your other question, instead of making doctrine return SQL's I just use my database (MySQL) to log all queries (since its just development environment).
try a var_dump() of your $single_user. If it is "null", it doens't exist ?
Also check if "Users" is a valid Entity name (no namespace?), and does the $id reference the PK of the user?
If you want to see the queries that are executed check your mysql/sql/... log or look into Doctrine\DBAL\Logging\EchoSQLLogger

Categories