Problem: When editing an entry SQL is making an INSERT instead of an UPDATE.
I have two SQL tables : users and users_detail.
users(id,role,name,password) &
users_detail(id,adress,city,user_id)
Foreign key users_detail.user_id is linked to users.id.
I have a form in my cakephp app to edit users_detail, if a user wants to edit his adress for example.
Here is my controller :
public function admin_edit_dashboard($id = null){
if (!$this->User->exists($id)) {
throw new NotFoundException('Invalid user details');
}
if ($this->request->is('post') || $this->request->is('put')) {
if ($this->User->UserDetail->save($this->request->data, true, ['id', 'address', 'city'])) {
$this->Session->setFlash('The user has been saved');
return $this->redirect(array('action' => 'dashboard'));
} else {
$this->Session->setFlash('The user could not be saved. Please, try again.');
}
} else {
//$this->request->data = $this->User->read(null, $id);
$user = $this->User->UserDetail->find('first', array(
'conditions' => array(
'UserDetail.user_id' => $id
)
));
$this->request->data = $user;
}
$this->set(compact('user'));
}
And my form :
<?php echo $this->Form->create('UserDetail');?>
<?php echo $this->Form->input('address', array('class' => 'form-control')); ?>
<br />
<?php echo $this->Form->input('city', array('class' => 'form-control')); ?>
<br />
<?php echo $this->Form->button('Submit', array('class' => 'btn btn-primary')); ?>
<?php echo $this->Form->end(); ?>
But when I'm validate the form to edit details I have an error :
Error: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '0' for key 'PRIMARY'
Because the SQL query isn't an UPDATE but an INSERT. I don't know why.
SQL Query: INSERT INTO cake.user_details
(adress_ex, city_ex) VALUES ('roses street',
'London')
Thank you!
EDIT : It works, thanks for your help! Good code added.
You appear to have several problems.
$this->request->address is in correct. Form data is passed as $this->request->data so you should be using $this->request->data['UserDetail']['address'] etc.
If you want to make sure you only save specific fields you can pass these as the third parameter of save() and then you don't need to use set() beforehand:-
$this->UserDetail->save($this->request->data, true, ['id', 'address', 'city']);
The SQL error implies that your primary key on the user_details table is not set to auto-increment (which it should). Which is why an INSERT fails.
You are also forgetting to pass the primary key for the record you're trying to update so Cake assumes you want to create a new record. Make sure you include this so that Cake knows to use UPDATE. For example include the following in your View form:-
echo $this->Form->hidden('id');
Your User model should have association to UserDetail like:
public $hasOne = array('UserDetail');
and your in UserDetail:
public $belongsTo = array('User');
Then you don't have to use loadModel, just do:
$this->User->UserDetail->find('first', array(...));
And you should edit user details from UserDetailsController action.
In your view add lines to know what you are editing:
echo $this->Form->input('id');
And then just do save with passed $this->request->data. That should do it. And like I said, check table id in database, it has to be autoincrement.
You can't update just your table by telling in controller
$this->UserDetail->save($this->request->data)
Instead you should try to do something like
//find that specific row in database and then update columns and save it
$this->UserDetail->set(array('address'=>$request->address,'city'=>$request->city));
$this->UserDetail->save();
Your model wants to save it like a new user_details under id = 0 which already exists because you didn't specify that you want to update it and which columns you want to update.
Hope it helps
Related
I am working on a personal Yii2 project and I am stuck! I don't know how to :
create a sing up page which has a form to create two related models (Organization & Employee ) and use a third one (Employee_Role)
Store these info in a session and use that session later.
General Scenario :
Admin signups by filling :
an Organization name
and username & password & email (for the Admin employee)
if the values are valid then the system creates "Organization" with an auto_generated organization_id.
Then, System creates "Employee" (which will need organization_id and assign this user to Admin "Employee_Role")
Then, the system keeps the following info in session: (organization_id, employee_id, role_id, timestamp ) and takes the user to admin home page.
Note, I am keeping the Models in the Common folder and the Controllers in the front-end and so should be the Views.
I appreciate your help,
This is a general outline of what you can do to solve your problem. You will need to add the actual fields to the form for both Organization and Employee.
The controller action:
public function actionSignup() {
$post = Yii::$app->request->post();
$organization = new Organization();
$employee = new Employee();
// we try to load both models with the info we get from post
if($organization->load($organization) && $employee->load($organization)) {
// we begin a db transaction
$transaction = $organization->db->beginTransaction();
try {
// Try to save the organization
if(!$organization->save()) {
throw new \Exception( 'Saving Organization Error' );
}
// Assign the organization id and other values to the employee
$employee->organization_id = $organization->id;
...
// Try to save the employee
if(!$employee->save()) {
throw new \Exception( 'Saving Employee Error' );
}
// We use setFlash to set values in the session.
// Important to add a 3rd param as false if you want to access these values without deleting them.
// But then you need to manually delete them using removeFlash('organization_id')
// You can use getFlash('organization_id'); somewhere else to get the value saved in the session.
Yii::$app->session->setFlash('organization_id', $organization->id)
Yii::$app->session->setFlash('employee_id', $employee->id)
...
// we finally commit the db transaction
$transaction->commit();
return $this->redirect(['somewhere_else']);
}
catch(\Exception e) {
// If we get any exception we do a rollback on the db transaction
$transaction->rollback();
}
}
return $this->render('the_view', [
'organization' => $organization,
'employee' => $employee,
]);
}
The view file:
<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;
?>
<?php $form = ActiveForm::begin() ?>
<?= $form->field($organization, 'some_organization_field') ?>
<!-- More Fields -->
<?= $form->field($employee, 'some_employee_field') ?>
<!-- More Fields -->
<?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
<?php ActiveForm::end() ?>
I need help with tracing this web application. I'm very new to Yii, and I'm trying to dissect an existing app to understand it better. I'm trying to create an edit function, which video tutorials lead me to believe has the exact same process as an add function [save()], except you specify the ID to be overwritten (and I very well could be wrong about this).
Near as I can tell, the following files are in play:
views/forum/view.php
views/forum/_commentform.php
views/forum/_comments.php
controllers/ForumController.php
models/Forum.php
models/Comment.php
I can't really change much of the existing, though I can add my own. It starts with view.php, where much of the stuff is displayed. At the bottom of it is this:
<?php
$this->widget('zii.widgets.CListView',
array('dataProvider'=>$dataProvider, 'itemView'=>'_comments', 'summaryText'=>'',));
?>
_comments.php displays all the usual elements of a comment, like say, from Facebook. There's an edit button there that I made, code here:
<?php echo CHtml::link(CHtml::encode('Edit'),array('forum/editcomment','reply'=>$data->id,'topic'=>$data->content_id)); ?>
That edit button gets the ID of the current comment from the database. Near as the application logs can tell me, this does work.
That calls this particular function in ForumController.php:
public function actionEditComment() {
if(isset($_GET['reply'])) {
$comment=Comment::model()->findByAttributes(array('id'=>$_GET['reply']));
$topic=Forum::model()->findByAttributes(array('id'=>$comment->content_id));
$this->renderPartial('_commentform', array('forum'=>$topic, 'model'=>$comment, 'view'=>'view',));
}
}
Next is the _commentform.php. Nothing much, just a textbox, though it does check if an ID is present; if it is, it is editing an existing comment, otherwise, it is creating a new one. A submit button also changes from Reply to Update, depending on the value of isNewRecord.
EDIT: There's also a CActiveForm, in case that of any help. Might have something to do with routing?
<?php $form=$this->beginWidget('CActiveForm', array(
'id'=>'comment-form',
'action'=>Yii::app()->createUrl('forum/view/id/'.$forum->id),
'enableAjaxValidation'=>false,
)); ?>
<?php
if ($view == 'view') {
echo CHtml::submitButton($model->isNewRecord ? 'Reply' : 'Update', array('id'=>'comment'.$model->id));
}?>
Again, confirmed via application logs, the ID of the comment is being passed through, albeit as id => comment<commentID>. Then this is where things get hazy. I assume the flow goes back to ForumController.php, where, per my logging, the ID is lost.
Here's the parts of the ForumController.php that I deem responsible:
public function actionView() {
$post=$this->loadModel();
$comment=$this->newComment($post);
$viewcount=$post->view_count+1;
$post->view_count=$viewcount;
$post->save();
$this->render('view',array('model'=>$post, 'dataProvider'=>$dataProvider,));
}
private $_model;
public function loadModel() {
if($this->_model===null) {
if(isset($_GET['id'])) {
$this->_model=Forum::model()->findByPk($_GET['id'], $condition);
}
if($this->_model===null)
throw new CHttpException(404,'The requested page does not exist.');
}
return $this->_model;
}
protected function newComment($post) {
$comment=new Comment;
if(isset($_POST['Comment'])) {
$comment->attributes=$_POST['Comment'];
$post->addComment($comment);
}
return $comment;
}
Interestingly, if I write out $comment from newComment() out to the log, it does print out the edited comment (i.e., it prints out "john cena" if I edited the existing comment "who is champ?"), but $comment->id yields a null, which I assume is why instead of updating, the edited comment is saved as a new one.
As for the models, Forum.php and Comment.php strangely point to the same database table, because for some reason they decided to put Forums and Comments into one table. Forum.php also contains the actual addComment function (a placement I find weird), though by the time the flow gets there, the Comment ID is of course null, though the edited comment itself is there.
Where did I go wrong? Did I miss anything?
EDIT: Here's the attributes and rules for the Comment model:
public function attributeLabels() {
return array(
'id' => 'ID',
'node_type' => 'Node Type',
'party_id' => 'Party',
'category' => 'Category',
'title' => 'Title',
'content' => 'Content',
'date_created' => 'Create Time',
'date_modified' => 'Update Time',
'status' => 'Status',);
}
public function rules()
{
/* combine parent and own rules */
$parentRules = parent::rules();
$myRules = array(
array('node_type_id', 'default', 'value'=>'7'), /* set type to Person */
array('node_type_id', 'in', 'range'=>array('7')), /* allow only Person type */
array('party_id, date_created, date_modified, status', 'numerical', 'integerOnly'=>true),
array('category, title, content', 'safe'),
);
/* you want to apply parent rules last, delete them here if necessary */
return array_merge($myRules);
}
Could you post Comment class defenition here?
I think you don't have rule for "id" in Comment::rules(),
When rule for attribute is not defined attribute will be unsafe and you can't set it value by $comment->attributes, or you can change you code to something like:
if(isset($_POST['Comment']) && isset($_POST['Comment']['id'])) {
$comment = Comment::model()->findByPk($_POST['Comment']['id']);
$comment->attributes=$_POST['Comment'];
$post->addComment($comment);
}
I'm trying to validating my insert data in codeigniter
The problem is the code returning me a duplicate entry's page error. And i want to throw that failure to home page with error message.
Here is my code:
$data = array(
'heheId' => $this->input->post('heheId'),
'userId' => $this->input->post('userId')
);
$this->db->insert('tentarasaya',$data);
if ($this->db->affected_rows() > 0){
$this->session->set_flashdata('info', "Hore sukses");
} else {
$this->session->set_flashdata('danger', "Fail insert");
}
redirect('my_home');
Any answer?
Update:
Duplicate entry like this
Try this
$data = array(
'heheId' => $this->input->post('heheId'),
'userId' => $this->input->post('userId')
);
if (!$this->db->insert('tentarasaya',$data)) { # add if here
# Unsuccessful
$this->session->set_flashdata('danger', "Fail insert");
}
else{
# Success
$this->session->set_flashdata('info', "Hore sukses");
}
redirect('my_home');
make sure that auto increment option is selected in your database. I am assuming that user_id here is a primary key, no need to insert that into a database yourself.
data = array(
'heheId' => $this->input->post('heheId'),
);
$this->db->insert('tentarasaya',$data);
Otherwise if you wish to insert user_ids yourself you can 1) define a custom call back function to check to see if the specified user id already exists in the database, 2) use the is_unique form validation rule that comes bundled with codeigniter
I have a table called a_forms, and I'm trying to create a drop down menu on a page of AForms that has a list of all the rows of a_forms. I want each choice in the drop down to link directly to the view of that form. Here is my forms table:
fid int(3) unsigned auto_increment (primary key)
title varchar(100)
created timestamp CURRENT_TIMESTAMP on update
modified timestamp
I was able to get as far populating the drop down menu, however I'm having trouble with the second part - linking each choice with the view selected. I'm a beginner to CakePHP so I am probably missing something. Here is the function in AFormsController:
public function forms($id = null)
{
$query = $this->AssessmentForms->find('all');
$result = $query->toArray();
$assessmentForm = $this->AssessmentForms->get($id, [ 'contain' => [] ]);
$this->set('assessmentForm', $assessmentForm);
$this->set('_serialize', ['assessmentForm']);
$data = $this->AssessmentForms->find('list', array('fields' => array('fid', 'title')));
$this->set('forms', $data);
}
forms.ctp:
<?php $this->Form->input('aform.select one', ['type' => 'select', 'options' => $forms, 'empty' => 'Choose One', 'onchange' => 'this.form.submit();']);?>
That gives me "Record not found in table "a_forms" with primary key [NULL]"
I also tried:
<?php $this->Form->create('Forms', array( 'url' => ['controller'=>'a_forms', 'action'=>'view', $aForm->fid])); ?>
And added this to the controller:
$aForms = $this->AForms->find('list');
It doesn't throw an error but I get a "Undefined variable: aForms [APP/Template/AForms/forms.ctp, line 10" in the view
I saw other people struggling with the 'onchange' method so I'm not sure if that's the right approach. I'm using version 3.1.0. Let me know if you need any more information!
Still don't quite know how to appropriately create queries with find(), but I found a way around with:
<ul class="dropdown-menu">
<?php foreach ($aForms as $aForm): ?>
<li> <?= $this->Html->link($aForm->title, ['action' => 'fill', $aForm->fid]) ?></li>
<?php endforeach; ?>
</ul>
And this in the controller:
$this->set('assessmentForms', $this->paginate($this->AssessmentForms));
$this->set('_serialize', ['assessmentForms']);
I pulled that directly from the bake-generated index() function. If anyone has a more syntactically non-hackish way of solving this, please let me know!
In my form, I created the value by populating the dropbox from values from a table.
<?php echo $form->dropDownList($model,'status', CHtml::listData(Statusprospect::model()->findAll(), 'id', 'status'),array('prompt' => 'Select')); ?>
When I view the record it has a 1, as it should for status. How do I make it display the value when the record is viewed, instead of the 1.
The view file code that currently displays the field is this:
<?php echo CHtml::encode($data->status); ?>
The Model does have the relationship defined:
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'status0' => array(self::BELONGS_TO, 'Statusprospect', 'status'),
);
}
How would I accomplish showing the value instead of the number?
Right now this should work $data->status0->status.
Take care that $data->status0->status might not be set if $data->status can be null so make a check beforehand if that is the case. You can use
CHtml::encode(isset($data->status0->status) ? $data->status0->status : '-');