Since we resolved my last problem (special thanks to ChandlerTi) I've been trying to update my code, to edit and safely delete database records using ZF2 and Doctrine. Now I've stuck on the edit function related problem. When i click on the edit button form opens and is correctly hydrated with record data. Since then, everything is okay. But on save button i receive an error:
C:\Apache24\htdocs\Helpdesk\vendor\doctrine\dbal\lib\Doctrine\DBAL\DBALException.php:47
An exception occurred while executing 'INSERT INTO tbl_incidents (creation_timestamp, engineer, reporter, description, urgency_level_id, status_id) VALUES (?, ?, ?, ?, ?, ?)' with params [null, null, null, null, null, null]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'description' cannot be null
Ofcourse, thats correct - description should not be null. Bu it is'nt! Ive made a trap to catch POST data sent to sript. They looks to be correct:
POST http://helpdesk.local/incident/add HTTP/1.1 Host: helpdesk.local User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: pl,en-us;q=0.7,en;q=0.3 Accept-Encoding: gzip, deflate Referer: http://helpdesk.local/incident/edit/7 Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 75 id=7&reporter=ttt&engineer=ttt&description=ttt&status=1&urgency=1&submit=Go
All the required fields are filled. Description to. Where is the problem? The only difference between this POST data and the POST generated by the add script (that works correctly) is that add scipt uses PHPSESSID var. And one more thing: my edit code is the same that add code:
IncidentController.php
public function editAction()
{
$incident = new Incident();
if ($this->params('id') > 0) {
$incident = $this->getEntityManager()->getRepository('Helpdesk\Entity\Incident')->find($this->params('id'));
}
$form = new IncidentForm($this->getEntityManager());
$form->setHydrator(new DoctrineObject($this->getEntityManager(),'Helpdesk\Entity\Incident'));
$form->bind($incident);
$request = $this->getRequest();
echo $request;
if ($request->isPost()) {
$form->setInputFilter($incident->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$em = $this->getEntityManager();
$em->persist($incident);
$em->flush();
$this->flashMessenger()->addSuccessMessage('Incident saved');
// Redirect to list of incidents
//return $this->redirect()->toRoute('incident');
}
}
$viewModel = new ViewModel(array(
'incident' => $incident,
'form' => $form,
));
$viewModel->setTemplate('helpdesk/incident/add.phtml');
return $viewModel;
}
/**
* Add action
*
*/
public function addAction()
{
return $this->editAction();
}
I completly dont know what is the difference here between add and edit. A why edit action uses SQL INSERT statement instead of UPDATE. Thanks for any help
Smok.
There are multiple things that you are missing or i think you are doing wrong here, i am saying this as i have no info on what your previous problem was.
1) you are creating a new object $incident = new Incident(); in EDIT
where as you need to do something like this, new object will always create empty object with no values
if (!$this->params('id')) {
$incident = new Incident(); // means you are coming from add action(which i think is not a good approach the way you are handling it)
}else{
$incident = $this->getEntityManager()->getRepository('Helpdesk\Entity\Incident')->find($this->params('id'));}
You will only need
2) I don’t know why you are passing
$form = new IncidentForm($this->getEntityManager());
Entity manager object to form? if you have specific reason please update your question else i don’t think it is required here. Coz seemingly you are making things tough for your self.
3) Try removing following line
$form->setHydrator(new DoctrineObject($this->getEntityManager(),'Helpdesk\Entity\Incident'));
4) try replacing this line only if add and edit Template are not identical
$viewModel->setTemplate('helpdesk/incident/add.phtml');// with
$viewModel->setTemplate('helpdesk/incident/edit.phtml');// if you dont have edit.phtml create it too
why as i assume your add action would have set its Form action and you will always render a Request similar to ADD.
5) This is my personal opinion that doing some thing like this is bad you need to create proper clean code for every action, for simpler scenarios you may find a work around for little bit tough forms you will stuck and you will have to rewrite whole code
/**
* Add action
*
*/
public function addAction()
{
return $this->editAction();
}
Related
var a = $v('P1995_LUMBER');
if ((a = '1')) {
apex.submit({
request: "CREATE",
set: {
LUMBER: "P1995_LUMBER",
LST_NME: "P1995_LST_NME",
FST_NME: "P1995_FST_NME",
},
});
} else if (a != '1') {
apex.submit({
request: "Update",
set: {
LUMBER: "P1995_LUMBER",
LST_NME: "P1995_LST_NME",
FST_NME: "P1995_FST_NME",
},
});
} else {
alert("bang bang");
}
Couple of things:
JavaScript's equality check is either == or === (more details here). (a = '1') assign '1' to the variable.
It seems like you're not using the apex.submit process correctly. Typically, you would set the item's value
e.g.:
apex.page.submit({
request: "SAVE",
set: {
"P1_DEPTNO": 10,
"P1_EMPNO": 5433
}
} );
Although, by looking at your JavaScript code, I would say you don't even need to use JavaScript.
Whenever you submit a page, all items on it are automatically sent to the server-side. You can then reference them using bind variables. You could then simply have two process, one for the Create and one for the Update, each having the corresponding insert/update statement using the different items on your page.
Usually what you will see is a page with two buttons for Create/Edit. They will have a server-side condition so that only the correct one is displayed.
Try creating a Form type page (form with report) using the wizard, and you'll see how everything is done.
Without seeing the page and the code you're using it's hard to tell what your issue really is, more details would be required.
That code does not have any sql in it so it is impossible to diagnose why you are encountering a TOO_MANY_ROWS exception. Run the page in debug mode and check the debug data - it should show you what statement is throwing the exception. If you need more help, post a proper reproducible case, not a single snipped of code without any context.
I'm looking at a new Symfony5 project , where I'm trying to return a JSON response of some data.
I have a Project and a ProjectItem
I have the following:
// Project.php
/**
* #ORM\OneToMany(targetEntity="App\Entity\ProjectItem", mappedBy="project")
*/
private $projectItems;
// ProjectItem.php
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Project", inversedBy="projectItems")
*/
private $project;
I have one Project, that can have many ProjectItems
I then have a controller that I'm trying to return a json response:
public function index()
{
$itemsList = $this->getDoctrine()
->getRepository(Project::class)
->findAll();
$items = $this->get('serializer')->serialize($itemsList, 'json');
return new Response($items, 200);
}
This is currently returning an error:
A circular reference has been detected when serializing the object of class "App\Entity\Project" (configured limit: 1)
Am I using the serializer correctly or are my models incorrectly configured?
Simply use json_encode:
public function index()
{
$itemsList = $this->getDoctrine()
->getRepository(Project::class)
->findAll();
return new Response(
json_encode($itemsList),
200
);
}
or use JsonResponse class:
return new JsonResponse($itemsList);
You have a circular reference with your relations. Im guessing ProjectItem has a field project that is referencing Project which causes a loop for the serializer. You can ignore said attribute to prevent this from happening. Checkout the ignored attributes section of the serializer documentation.
Another option would be to use Serialization Groups. Every property would get a Group annotation like for example #Groups("group1") excluding that reference property back to Project.
You would then tell the serializer to serialize that group:
$json = $serializer->serialize(
$itemList,
'json', ['groups' => 'group1']
);
You may also checkout JMS Serializer which adds #Exclude and #Include annotations to make this step a bit easier.
I use the following code to perform sub-request in HMVC structure:
A request to "page1" will make a sub-request to "page2" by the following code:
$request = Request::factory('/page2')
->method(Request::POST)
->post($postData)
->execute();
The execution in "page2" will add / change the value of a cookies item by
setcookie('new_var', $newValue);
Now I need to capture the new value of the cookie "new_var" in "Page1". So how can I do that?
PS: Due to some limitations, I have to set the 'new_var' in cookie, so putting it to session is not an answer.
==========update =============
As suggested by zerkms, I did something like this:
$response = Request::factory('/page2')
->method(Request::POST)
->post($postData);
//before
error_log(print_r($response->cookie(), TRUE));
$response->execute();
//after
error_log(print_r($response->cookie(), TRUE));
the result of the "before" and "after" log entries are both empty array. :(
In kohana you'd better used Response::cookie() method.
In this case you can use this method for both retrieving and setting cookies (even in the same request)
I've found a few questions and pages dealing with cookies in Symfony2 but there doesn't seem to be any clear consensus on exactly how this is supposed to work. I can, of course, just fall back to using PHP's native setcookie function but I feel that it should be an easy thing to do with Symfony2 as well.
I have an action in my controller from which I simply want to return a view with a cookie attached. Thus far I have seem examples basically like this:
use Symfony\Compentnt\HttpFoundation\Response;
public function indexAction() {
$response = new Response();
$response->headers->setCookie(new Cookie('name', 'value', 0, '/');
$response->send();
}
The problem with this is that it sends the response... and doesn't render the view. If I set the cookie without sending the headers the view is rendered but the header (cookie) is not sent.
Poking around I found the sendHeaders() method in the Response object so I'm now manually calling that in my action before returning and that seems to work:
public function indexAction() {
...
$response->sendHeaders();
return array('variables' => 'values');
}
But is this really the expected pattern to use? In previous versions of symfony I could set the headers in my controller and expect the view controller to handle sending whatever I had sent. It seems now that I must manually send them from the action to get it to work, meaning I have to call this from any action that I set headers in. Is this the case or is there something that I'm missing that's so obvious that no one has bothered to even mention it in any of the documentation?
I think you're on the right lines with:
$response->headers->setCookie(new Cookie('name', 'value', 0, '/'));
If you're trying to render a template then check out the docs here:
Symfony2 Templating Service
If you look at the line:
return $this->render('AcmeArticleBundle:Article:index.html.twig');
basically the render method is returning a response (which the controller then returns) which has the content of the twig template, all you have to do is intercept this:
$response = $this->render('AcmeArticleBundle:Article:index.html.twig');
$response->headers->setCookie(new Cookie('name', 'value', 0, '/'));
return $response;
I think that's it anyway...
i tried to implement the file upload via doctrine/lifecycle callbacks as described here:
http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html#using-lifecycle-callbacks
So far it works, but the PrePersist/PreUpdate Event is not fired, the function "preUpload" is not called.
Functions like "upload" and "removeUpload" triggered by other lifecycle events are called correctly.
Does anyone have an idea why the event is not fired or a solution for this problem?
Thanks
I have another solution to this problem:
My entity has a field "updatedAt" which is a timestamp of the last update. Since this field gets set anyway (by the timestampable extension of Gedmo) I just use this field to trick doctrine into believing that the entitiy was updated.
Before I persist the entity I set this field manually doing
if( $editForm['file']->getData() )
$entity->setUpdateAt(new \DateTime());
This way the entity gets persisted (because it has changed) and the preUpdate and postUpdate functions are called properly.
Of course this only works if your entity has a field that you can exploit like that.
You need to change tracking policies.
Full explanation.
there's a much simpler solution compared with changing tracking policies and other solutions:
in controller:
if ($form->isValid()) {
...
if ($form->get('file')->getData() != NULL) {//user have uploaded a new file
$file = $form->get('file')->getData();//get 'UploadedFile' object
$news->setPath($file->getClientOriginalName());//change field that holds file's path in db to a temporary value,i.e original file name uploaded by user
}
...
}
this way you have changed a persisted field (here it is path field), so PreUpdate() & PostUpdate() are triggered then you should change path field value to any thing you like (i.e timestamp) in PreUpdate() function so in the end correct value is persisted to DB.
A trick could be to modify the entity no matter what..on postLoad.
1 Create an updatedAt field.
/**
* Date/Time of the update
*
* #var \Datetime
* #ORM\Column(name="updated_at", type="datetime")
*/
private $updatedAt;
2 Create a postLoad() function that will modify your entity anyway:
/**
* #ORM\PostLoad()
*/
public function postLoad()
{
$this->updatedAt = new \DateTime();
}
3 Just update that field correctly on prePersist:
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
$this->updatedAt = new \DateTime();
//...update your picture
}
This is basically a slight variation of #philipphoffmann's answer:
What i do is that i modify an attribute before persisting to trigger the preUpdate event, then i undo this modification in the listener:
$entity->setToken($entity->getToken()."_tmp");
$em->flush();
In my listener:
public function preUpdate(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof MyEntity) {
$entity->setToken(str_replace('_tmp', '', $entity->getToken()));
//...
}
}
Another option is to display the database field where the filename is stored as a hidden input field and when the file upload input changes set that to empty so it ends up triggering doctrine's update events. So in the form builder you could have something like this:
->add('path', 'text', array('required' => false,'label' => 'Photo file name', 'attr' => array('class' => 'invisible')))
->add('file', 'file', array('label' => 'Photo', 'attr' => array('class' => 'uploader','data-target' => 'iddp_rorschachbundle_institutiontype_path')))
Path is a property managed by doctrine (equal to the field name in the db table) and file is the virtual property to handle uploads (not managed by doctrine). The css class simply sets the display to none. And then a simple js to change the value of the hidden input field
$('.uploader').change(function(){
var t = $(this).attr('data-target');
//clear input value
$("#"+t).val('');
});
For me, it worked good when I just manually called these methods in the controller.
Do you have checked your metadata cache driver option in your config.yml file?If it exists, just try to comment this line:
metadata_cache_driver: whateverTheStorage
Like this:
#metadata_cache_driver: whateverTheStorage