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
Related
I am using the Laravel framework to work with my MySQL database, and currently want to update my database from a JSON object, that will be sent from somewhere else.
Currently, I have it the same as my 'Store' function, which is obviously not going to work, because it will update everything, or refuse to work because it is missing information.
This is the for each I have currently, it does not work, but I am not experienced with how it is best to parse a JSON with a for-each, then store it.
public function update(Request $request,$student)
{
$storeData = User::find($student);
foreach ($request as $value) {
$storeData-> username = $value;
}
Here is my store function, with all the info that the front-end team may send in a JSON format.
$storeData->username=$request->input('username');
$storeData->password=$request->input('password');
$storeData->email=$request->input('email');
$storeData->location=$request->input('location');
$storeData->role=DB::table('users')->where('user_id', $student)->value('role');
$storeData->devotional_id=$request->input('devotional_id');
$storeData->gift_id=$request->input('gift_id');
$storeData->save();
return dd("Info Recieved");
You can write the method like the below snippet.
Also, assume you are working with laravel API, so you don't need to parse the incoming JSON input, but you will receive these values as items in the request object.
However, you should use the filled method in order to determine if the field is existing and has a value, the update function will override with empty values otherwise.
I just added this method to the first input, but you have to use it each and every input if you are not sure what the front end will pass.
public function update(Request $request, $student)
{
$storeData = User::find($student); // should be id
if ($request->filled('username')) { // use this for other items also
$storeData->username = $request->input('username');
}
$storeData->password = $request->input('password');
$storeData->email = $request->input('email');
$storeData->location = $request->input('location');
$storeData->role = DB::table('users')->where('user_id', $student)->value('role');
$storeData->devotional_id = $request->input('devotional_id');
$storeData->gift_id = $request->input('gift_id');
$storeData->update();
dd("Info Recieved");
}
Why would they send json data from the front in a post?
Really it would be from a form input. Like Rinto said it would be request object.
$user->username = $request->user_name;
I'm gathering this is a form on the front to create a new user. Why not use the built in auth scaffolding that has this set up for you in the register area?
So I'd personally use...
//look up user that matches the email or create a new user
$user = User::firstOrNew(['email' => request('email')]);
//add other input values here
$user->name = request('name');
//save
$user->save();
Hard to give an exact answer to this when the question is a bit vague in what you're doing. There are many methods in Laravel to accomplish things. From your code it just looks like registration. Also, the big gotcha I see in your code is you are passing a text only password and then adding that password in plain text to your database. That is a big security flaw.
you can convert your JSON object to an array and then do your foreach loop on the new array. To update a table in Laravel it's update ($storeData->update();) not save. Save is to insert.
$Arr = json_decode($request, true);
I'm implementing editing user profile via API. The page where user edits its data contains a lot of fields, but when user submits the form, only edited fields are sent to my API endpoint. Also I'm not using form mapping.
Only way I see is to write something like this:
public function editProfile(FormInterface $form, User $user): User
{
$args = $form->getData();
if ($args['email']) {
$user->setEmail($args['email']);
}
if ($args['phone']) {
$user->setPhone($args['phone']);
}
// ...
$this->em->persist($user);
$this->em->flush();
return $user;
}
But it looks terrible and my form may contain up to several tens of fields.
Does anybody know good solution for this case?
Use form mapping and submit form with disabled clear missing fields option:
In form builder:
$options->setDefaults([
'data_class' => MyEntity:class
]);
In controller:
$data = $request->request->all();
$form->submit($data, false);`
instead of $form->handleRequest($request);
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(),
)
);
}
}
I am getting PDOException in edit album "SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'artist' cannot be null". I debugged the code and found that after edit form action runs all the column(id,title,artist) values change to the null value in the insert statement, whereas it should be POST values of the edit form. I am using the same code as of ZF2 tutorial.
$request->getPost() has correct edited values but $form->getData() returns empty form post values for (id,title,artist).
can anybody please help.
My code is:
public function editAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('album', array(
'action' => 'add'
));
}
$album = $this->getAlbumTable()->getAlbum($id);
$form = new AlbumForm();
$form->bind($album);
$form->get('submit')->setAttribute('value', 'Edit');
$request = $this->getRequest();
if ($request->isPost()) {
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getAlbumTable()->saveAlbum($form->getData());
// Redirect to list of albums
return $this->redirect()->toRoute('album');
}
}
return array(
'id' => $id,
'form' => $form,
);
}
According to the ZF2 example, shouldn't it be
$this->getAlbumTable()->saveAlbum($album);
instead of
$this->getAlbumTable()->saveAlbum($form->getData());
Because you have already bind the $album which attaches the model to the form. This basically does two things
Displays the initial values fetched from that Album with unique ID
After validation of the form the data is put back into the model.
Just try what I have suggested
Perhaps you were experiencing the same issue as I did when creating an Entity (Model object) that was to be bound to the Form object.
The mistake I made was that I was providing always a new instance of the InputFilter from the entity's method
getInputFilter();
And after calling $form->isValid(), Zend Form was actually looking to see if there is an entity object bound to it...if so, then it would call the $entity->getInputFilter(), inside the form's $this->bindValues() method and after receiving the filter object the code would return $filter->getValues() to populate the bound model. Since the entity was always returning new InputFilter instance, naturally the values were empty/null.
For me, the mistake was writing something like this in the entity(Model):
public function getInputFilter()
{
return new SomeInputFilter();
}
But actually, I needed to write the method like this:
public function getInputFilter()
{
if(empty($this->inputFilter)){
$this->inputFilter = new SomeInputFilter();
}
return $this->inputFilter;
}
As you can see, the solution was to set a protected property $inputFilter, and populate it with a new instance of the InputFilter object only if it's empty. Didn't pay attention to the docs thoroughly while I was coding, and was having the same issue as you did (empty data in the bound model), while trying to insert a record.
Hopefully, you'll find this useful, if not however, I'm sorry to waste your time reading this. :)
P.S.: Thank you for reading my answer and I know I am a little late with the response to the topic, but I've recently started working with Zend 2 Framework, and I've experienced a similar issue, so I tried to share my 2 cents in the hope of helping somehow if possible.
I am building an application where the user can edit some data and then gets presented with a screen where he can confirm (and comment on) his edits.
In the confirmation form I display the changes that have been made to the entity. This works for "normal" fields. Here is some code that works for checking a single field:
// create $form
// bind $form
if ($form->isValid() {
$data = $form->getData();
// example, get changes of a "normal" field
if ($data['color'] != $entity->getColor()) {
// do something with changes
}
}
But I can't do the same for a relation (example ManyToMany with Users) :
if ($data['users'] != $entity->getUsers()
doesn't work because $data['users'] and $entity->getUsers() refer to the same persistent collection. It is possible to call this function to see if there are changes:
if ($data['users']->isDirty())
but it isn't possible to see what changes were made.
The second problem with the above is that if all items are removed from the persistent collection, Doctrine does not mark it as "changed" (isDirty() = true), so I can't catch the specific change where the user removes all "users" from the entity in the form.
Please note that the code all works, the only problem I have is that I am unable to view/process the changes made on the confirmation step.
Doctrine\ORM\PersistentCollection has internal API (public) methods getSnapshot, getDeleteDiff, getInsertDiff that can be used during lifecycle events of the Doctrine\ORM\UnitOfWork. You could for example check the insert diff of a persistent collection during onFlush.
Solved it like this:
1) To get changes that will be made directly to the Entity, use the following:
// create form
// bind form
// form isValid()
$uow = $em->getUnitOfWork();
$uow->computeChangeSets();
$changeset = $uow->getEntityChangeSet($entity);
print_r($changeset);
2a) To get changes to the relations, use the answer from Lighthart above:
$oldUsers = $entity->getUsers()->toArray();
// bind form
// form isValid
$newUsers = $entity->getUsers()->toArray();
// compare $oldUsers and $newUsers
2b) Use these methods on Persistent Collection to find inserts / deletes:
$newUsers = $entity->getUsers();
$inserted = $newUsers->getDeleteDiff();
$deleted = $newUsers->getInsertDiff();
The only problem with (2b) is that if ALL users are removed and none added then getDeleteDiff() is empty which appears to be a Doctrine bug/idiosyncrasy
Store the original collection in a variable before bind and then compared the new collection after bind. PHP has quite a few array comparison functions, and collections are readily turned into native arrays by $collection->toArray();
eg:
// create form
$oldusers=$entity->getUsers()->toArray();
// bind form
if ($form->isValid() {
$data = $form->getData();
if ($data['users'] != $oldusers) {
// do something with changes
}
}