I'm trying to submit a form (it's a dynamic form with fields added via jQuery) to CodeIgniter for a db insert. Part of it works, the other part doesn't.
Here's the jQuery:
function submitForm() {
$.ajax({
type: 'POST',
url: '/raffle/save/',
data: $('#raffle').serialize(),
success: function (response) {
alert(response);
},
error: function() {
alert('Failed'); // This is what I get unless I comment out the entry insert
}
});
}
The CI controller:
class Raffle extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->model('raffle_model');
$this->load->library('form_validation');
}
public function index() {
$data['title'] = 'Create a Raffle';
$this->load->view('header', $data);
$this->load->view('raffles/create_view', $data);
$this->load->view('raffles/bottombar_view', $data);
$this->load->view('footer', $data);
}
public function save() {
foreach($_POST as $k => $v) {
if($k == 'entrant' || $k == 'tickets') {
foreach ($v as $i => $vector) {
$this->form_validation->set_rules('entrant[' . $i . ']', 'Entrant name', 'trim|required|min_length[1]|max_length[100]|xss_clean');
$this->form_validation->set_rules('tickets[' . $i . ']', 'Number of tickets', 'trim|required|max_length[2]|is_natural_no_zero|xss_clean');
}
} else {
$this->form_validation->set_rules('raffle-name', 'Raffle name', 'trim|required|min_length[4]|max_length[100]|xss_clean');
$this->form_validation->set_rules('winners', 'Number of winners', 'trim|required|max_length[2]|is_natural_no_zero|xss_clean');
}
}
if($this->form_validation->run() == FALSE) {
echo 'Validation failure!';
} else {
if($this->raffle_model->add_raffle()) { // It does pass validation and goes to the model
echo 'Data added successfully!';
}
}
}
}
And the CI model:
class Raffle_model extends CI_Model {
public function __construct() {
parent::__construct();
}
public function add_raffle() {
// This works
$meta = array(
'user_id' => $this->session->userdata('user_id'),
'name' => $this->input->post('raffle-name'),
'winners' => $this->input->post('winners'),
'created_ip' => $_SERVER['REMOTE_ADDR']
);
// This works and is a multidimensional array for insert_batch()
$entrants = array(
array(
'date' => date(DATE_ATOM),
'raffle_id' => '1'
)
);
foreach($_POST['entrant'] as $name => $n) {
array_push($entrants,
array(
'name' => $n,
'tickets' => $_POST['tickets'][$name]
)
);
}
$this->db->insert('raffle', $meta);
$this->db->insert_batch('entry', $entrants); // This one returns error 500
return true;
}
}
Here's the problem: Submitting the form, the meta portion does get saved to the raffle table, but the entrants portion does not get saved to the entry table. I have tried using a simple dummy array (sample data, no post data, no loops) to see if it would work, but it still doesn't. Console logs say POST http://rafflegrab.dev/raffle/save/ 500 (Internal Server Error).
CSRF is off in CI config.
The table is set up as follows:
Table name: entry
InnoDB
id - bigint(12) - UNSIGNED - not_null - AUTO_INCREMENT - PRIMARY
user_id - int(10) - UNSIGNED
name - varchar(100)
tickets - smallint(5) - UNSIGNED
date - datetime
raffle_id - int(10) - UNSIGNED
I'm assuming you're using unsafe input methods to be able to access $_POST ?
What happens if you mock up a noddy form with those fields and post it to the Save action on the controller?
Next question is the usual stuff but what do the server logs say? Have a look in the Event Log (windows) or /var/log/apache2/error_log (SUSE) or the equivalent on your system. It should tell you why it's not working.
If not, make sure your php log level is high enough to output errors.
If you're not in a production environment, consider displaying PHP errors until you've tracked down the problem
Edit: Always better to know the problem than guess but my first thought is that your 2-dimensional array as inconsistent. You seem to have:
array(
array('date'=>blah, 'raffleId'=>blah),
array('name'=>blah, 'tickets'=>blah),
array('name'=>blah, 'tickets'=>blah)
...
)
Did you in fact want
array(
array('date'=>blah, 'raffleId'=>blah, 'name'=>blah, 'tickets'=>blah),
array('date'=>blah, 'raffleId'=>blah, 'name'=>blah, 'tickets'=>blah),
array('date'=>blah, 'raffleId'=>blah, 'name'=>blah, 'tickets'=>blah),
...
)
? Perhaps something like:
$entrants = array();
$recordedDate = date(DATE_ATOM);
foreach($_POST['entrant'] as $name => $n) {
array_push($entrants,
array(
'date' => $recordedDate,
'raffle_id' => '1'
'name' => $n,
'tickets' => $_POST['tickets'][$name]
)
);
}
Related
On Store function I replicate() form data as following:
public function store(StoreNewsRequest $request)
{
$news = News::create($request->all());
if ($request->input('image', false)) {
$news->addMedia(storage_path('tmp/uploads/' . $request->input('image')))->toMediaCollection('image');
}
if ($media = $request->input('ck-media', false)) {
Media::whereIn('id', $media)->update(['model_id' => $news->id]);
}
$assigned_pack_ids = DB::table('content_pack_news')->where('news_id' , $news->id)->pluck('content_pack_id')->toArray();
foreach($assigned_pack_ids as $key => $pack_id) {
$news_replica = $news->replicate();
$news_replica->save();
DB::table('content_pack_news')->insert(['content_pack_id' => $pack_id,'news_id' => $news_replica->id]);
}
return redirect()->route('admin.news.index');
}
This testing code currently replicates the form data to 3 entries by foreach($assigned_pack_ids
But I get the uploaded image only on the first original entry.
How to copy the same image to all entries with different name for example image_name_assigned_pack_id and assign to the replicate data?
I'm using the https://wordpress.org/plugins/wp-session-manager/ plugin and it works if I just set and get variables. (so the actual session managment works)
The thing I want to is to exchange session-data between ajax-calls and "normal request to browser". Is this even possible?
functions.php
add_action( 'wp_ajax_nopriv_set_formvalues', array ('FormHandler', 'set_formvalues') );
add_action( 'wp_ajax_set_formvalues', array ('FormHandler', 'set_formvalues') );
//Use of Plugin WP Session manager (so we can use sessions for storing current form values)
//(do this with ajax before submitting the form)
global $wp_session;
$wp_session = WP_Session::get_instance();
$formhandler = new FormHandler();
class FormHandler {
public function get_current_formvalues() {
return $wp_session['formvalues'];
}
public function set_formvalues() {
//Default values for form
$formvalues = array('name' => '',
'address' => '',
'plz' => '',
'city' => '',
'phone' => '',
'fax' => '',
'contactfirstname' => '',
'contactlastname' => '',
'contactanrede' => '',
'email' => '',
'contactphone' => '',
'recall' => '',
'newsletter' => '',
'messageText' => '');
//Change values in formvalues-array if values are posted
foreach($formvalues as $key => $value) {
if (isset($_POST[$key])) {
$formvalues[$key] = $_POST[$key];
}
}
$wp_session['formvalues'] = $formvalues;
echo json_encode( $wp_session );
die(); //Needed for wordpress ajax
}
public function __construct() {
$formvalues = $this->get_current_formvalues();
echo $formvalues //doesn't echo out nothing
}
}
$n = new FormHandler(); //doesn't echo out anything
etc...
From javascript I call the set_formvalues() through ajax and the values are returned (as json) correctly.
I have to submit a form to another domain, but before I have to save the formvalues somehow because the domain I post redirects my pagebut only with a status code. My thought was I could sent these formvalues through ajax, and then fetch them at next time my page is accessed so the visitor won't have to fill in all values if the user makes one single mistake.
Is it possible to exchange between ajax-calls and normal request and then how would I achieve that? unfornutenaly I must live with the "redirection-solution from the other server". Maybe it's possible but what am I doing wrong then? Any ideas?
UPDATE
Even if I got it working with global variables, I wasn't satisfied with that. It really don't like globals, but I couldn't figure a way of doing this before, but now I've got a solution that I thought I wanted to share with you if someone stumbles upon similar issue(s):
class FormHandler {
private $wp_session = null; //Use of Plugin WP Session manger, so we can handle sessions
public function get_current_formvalues() {
return $this->wp_session['formvalues'];
}
public function set_formvalues() {
if ($this->wp_session === null) {
$this->wp_session = WP_Session::get_instance();
}
//set $formvalues based on posted fields...
$this->wp_session['formvalues'] = $formvalues;
echo json_encode( $this->wp_session );
die(); //Needed for wordpress ajax
}
public function __construct() {
//This makes it possible to use $this within function set_formvalue when using ajax in Wordpress
add_action( 'wp_ajax_nopriv_set_formvalues', array ( $this, 'set_formvalues') );
add_action( 'wp_ajax_set_formvalues', array ($this, 'set_formvalues') );
$this->wp_session = WP_Session::get_instance();
}
public function display_contactform() {
$formvalues = $this->get_current_formvalues();
}
}
Global keyword should be inside the function you want to use it in, like so:
public function get_current_formvalues() {
global $wp_session;
return $wp_session['formvalues'];
}
And also add it in the set method
public function set_formvalues() {
global $wp_session;
//Default values for form
The way it is currently written, $wp_session is not availble when called.
You can read more on http://php.net/manual/en/language.variables.scope.php
I am using the MultiForm module to submit a long form with SilverStripe. The logic for this form is in 'CampaignBriefForm.php' whereas the gridfield CMS field is being added in 'CampaignBriefPage.php'. I have a Data Object for a CampaignBriefLead which is what the form creates.
Campaign Brief Page
private static $has_many = array(
'CampaignBriefLeads' => 'CampaignBriefLead'
);
public function CampaignBriefForm() {
return new CampaignBriefForm($this, 'CampaignBriefForm');
}
Campaign Brief Lead (DO)
private static $has_one = array( "Page" => "CampaignBriefPage" );
As you can see the Campaign Brief page has the correct relationship with the Data Object and also you can see the the form itself (done in a sepearate file) is correctly returning (as it's being saved in the DB). For some reason however, the gridfield will not show me what is in the database for that Data Object. The grid field code is as follows.
$fields = parent::getCMSFields();
$contactConfig = GridFieldConfig_RelationEditor::create();
$contactConfig->getComponentByType('GridFieldDataColumns')->setDisplayFields(
array(
'CompanyName' => 'Company Name',
'StartDate' => 'Start Date',
'Duration' => 'Duration',
'WebsiteURL' => 'Website',
'Budget' => 'Budget'
));
$contactGrid = new GridField(
'CampaignBrief',
'Campaign Enquiries',
$this->CampaignBriefLeads(),
$contactConfig
);
$fields->addFieldToTab("Root.Enquiries", $contactGrid);
To me this all looks correct and should work but for some reason it is not working.
Note
The link existing option on the gridfield allows me to link one of the entries from the DO with the gridfield weirdly?? So it saves one entry but I have to do it manually, this tells me it can see the DB but won't pull for some reason.
For reviewing reasons, here is the code for the multiform where the campaign brief lead is actually saved to the DB after the form is submitted.
public function finish($data, $form) {
parent::finish($data, $form);
$steps = DataObject::get(
'MultiFormStep',
"SessionID = {$this->session->ID}"
);
$enquiry = new CampaignBriefLead();
foreach($steps as $step) {
$data = $step->loadData();
foreach($data as $key => $value) {
if($key == 'url' || $key == 'MultiFormSessionID' || $key == 'action_finish') {
continue;
}
if(isset($data[$key])) {
$enquiry->$key = $data[$key];
error_log($data[$key]);
}
}
}
$enquiry->write();
$this->controller->redirect('/campaign-brief/');
}
If you need anything more let me know. Thanks.
I would take a guess that the CampaignBriefLead PageID is not being set on your form submission.
Check the CampaignBriefLead table in your database and check the PageID column. If it is blank, null or 0 for each row then it is not being set.
One way to fix this problem for any new submission is to set the PageID for the $enquiry:
public function finish($data, $form) {
// ...
$enquiry = new CampaignBriefLead();
if ($campaignBriefPage = CampaignBriefPage::get()->first()) {
$enquiry->PageID = $campaignBriefPage->ID;
}
// ...
}
For the existing entries you will need to update the entries to have the correct PageID.
I am building a simple mechanism where a user can like a post by clicking on a link. I'm using GET rather than POST as I want to allow the method to fire via the URL.
That been said how do I save data using GET? As the request data doesn't exist in this scenario... My model looks like:
class Like extends AppModel
{
public $name = 'Like';
public $belongsTo = array('User','Post');
}
and the method for adding looks like:
public function add( $id )
{
$post = $this->Post->find('first', array(
'conditions' => array('Post.id'=>Tiny::reverseTiny($id))
));
if (!$post)
{
throw new NotFoundException('404');
}
if($post['Post']['user_id'] == $this->Auth->user('id'))
{
$this->Session->setFlash('You can\'t like your own post... That\'s just silly!');
}
if ($this->Like->create())
{
$liked = $this->Like->find('first', array(
'conditions' => array('Like.id'=>Tiny::reverseTiny($id), 'Like.user_id'=>$this->Auth->user('id') )
));
if(!$liked){
$this->Like->saveField('user_id', $this->Auth->user('id'));
$this->Like->saveField('post_id', $post['Post']['id']);
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
} else {
$this->Session->setFlash('You already like this post!');
}
else
{
$this->Session->setFlash('Server broke!');
}
}
Can anyone help?
<?php echo $this->Html->link('1', array('controller'=>'followers','action'=>'add','id'=>Tiny::toTiny($post['Post']['id'])),
array('title'=>'Follow','class'=>'follow')); ?>
This part all works fine. It's saving a new row in the DB on GET that I'm struggling with.
Hi you just need to make a link to your controller action and pass you variable in the url.
to be clear the link on the post to like is in your post view :
$this->Html->link('like this post', array('controller' => 'like', 'action' => 'add', $postId))
It should render a link like this :
www.yourWebSite/likes/add/1 to like the postId 1,
variables after your action (add) are interpreted as variable for your controller action
if your fonction add had been
public function add($postId, $wathever){
}
the url should look like www.yourWebSite/likes/add/1/blabla
where 1 is the first var for the add action and blabla the second one and so on.
this is the equivalent of a non rewriting url : ?postId=1&whatever=blabla
EDIT :
if(!$liked){
//simulate the post behaviour
$this->request->data['Like']['user_id'] = $this->Auth->user('id');
$this->request->data['Like']['post_id'] = $post['Post']['id'];
//save the data
if ($this->Like->save($this->request->data)) {
$this->Session->setFlash(__('Thanks for your support !'));
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
} else {
$this->Session->setFlash('Server broke!');
}
}
How about using save with id=0 instead of create?
<?php
$like = array(
"Like" => array
(
"id" => 0,
"user_id" => $this->Auth->user("id"),
"post_id" => $post['Post']['id']
)
);
$result = $this->Like->save($like);
if(!$result){$this->Session->setFlash('Server broke!');}
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
?>
I creter js file and add tbar add button when click one blnak row add in grid
in movies controller file i write
function ext_item($id = null) {
if(!empty($this->data)) {
if($this->Movie->save($this->data))
{
$this->set('success','true');
$this->data = array();
return;
}
else {
$this->set('success',"false");
return;
}
}
}
how to pass this js data ?
how to insert data in database?
in controller file
function create() {
$newData = json_decode($this->params['form'], true); // turn the incomin json into an array
$this->data = array(
'Movie' => array(
'date_' => $newData['date_'],
'notes' => $newData['notes'],
'asset_id' => $newData['asset_id'],
'maint_picture' => $newData['maint_picture'],
'maint_condition1' => $newData['maint_condition1'],
'maint_condition2' => $newData['maint_condition2'],
'maint_condition3' => $newData['maint_condition3'],
'maint_condition4' => $newData['maint_condition4'],
)
);
if ($this->Movie->save($this->data))
{
$data['success'] = true;
} else {
$data['success'] = false;
}
$this->set('data', $data);
//$this->layout = 'ajax';
return $this->render(null, null, '/movies/ext_item');
}
then in js file
var proxy = new Ext.data.HttpProxy({
api: {
// these will map to cakephp controller actions
create: { url: 'movies_controller/create', method: 'POST' },
// read: { url: '/movies_controller/index', method: 'POST' },
//update: { url: '/movies_controller/update', method: 'POST' },
destroy: { url: 'movies_controller/destroy', method: 'POST' }
}
});
and for add row in grid
tbar: [{
text: 'Add Movie',
icon: 'images/table_add.png',
cls: 'x-btn-text-icon',
handler: function() {
grid.getStore().insert(0, new Movie({
id: 0,
notes: 'New Movie',
asset: ''
}));
rowEditor.startEditing(0, true);
}
}]
What wrong with this. it's not insert data in database.
What you want to do is add to the grid using ExtJS. The store that is attached to your grid (if you follow my answer to your last question) will handle talking to the server.
In ExtJS, the button in your toolbar to add a row to your grid should have a handler.
var toolbar = Ext.Toolbar({
// config options
handler: function() {
// in your handler you need to create a new record and insert it into your store
// if you followed my answer to your last question, you'll have setup a store with proxy, jsonreader, and jsonwriter.
// get the store component from Ext
var store = Ext.getCmp('idOfYourStore'),
NewRecord = Ext.data.Record.create(['name', 'genre', 'length']); // this array of column names should match the fields you specified for your JsonReader's fields
// now that you have your store component, and your new blank record. you can fill it in and add it to the store
var record = new NewRecord({
name: 'Name of Movie',
genre: 'Genre of Movie',
length: '1:25:22'
});
store.add(record);
store.commitChanges();
}
});
After calling add (if autosave is set to true on your store) it will automatically call the url to your cakephp application that you setup in your proxy's api under 'create'. It will send the data of this new record to that action.
So if you set up you're create proxy to point to /movies/create than inside of your MoviesController you want to setup a create() action.
Inside of the create action, you'll want to check $this->params['form'] for the incoming data from ExtJS.
function create() {
$newData = json_decode($this->params['form'], true); // turn the incomin json into an array
$this->data = array(
'Movie' => array(
'name' => $newData['name'],
'genre' => $newData['genre'],
'length' => $newData['length']
)
);
if ($this->Movie->save($this->data)) {
$data['success'] = true;
} else {
$data['success'] = false;
}
return json_encode($data);
}
After ExtJs makes the post to PHP it expects a json object back with a 'success' key in the root of the object with true, or false. You need this in json, so you can't simply just use $this->set and send it to your view. In this case I'm returning the json_encoding string.
In reality what you should do, is include the Js helper in your app_controller. Then create an element named ajaxreturn. /views/elements/ajaxreturn.ctp would contain one line.
<?php echo $this->Js->object($data) ?>
Object is responsible for turn $data into a json object. It's used instead of json_encode because PHP4 didn't have support for json_encode.
now that you have this element, in your controller you can rewrite it like so...
function create() {
$newData = json_decode($this->params['form'], true); // turn the incomin json into an array
$this->data = array(
'Movie' => array(
'name' => $newData['name'],
'genre' => $newData['genre'],
'length' => $newData['length']
)
);
if ($this->Movie->save($this->data)) {
$data['success'] = true;
} else {
$data['success'] = false;
}
$this->set('data', $data);
$this->layout = 'ajax';
return $this->render(null, null, '/elements/ajaxreturn');
}
You want to return the json string and ONLY the json string. No layout, no html, nothing but the string it will throw an error.
Once you do this, your store will know whether the call was successful, if so it will stick a row in your grid. If not, it will delete the temp. row it put in your grid.
I'm not sure I understanding what you're asking for.
RequestHandler is how cake enables handling javascript/ajax requests:
http://book.cakephp.org/view/1291/Request-Handling