I have a Collection created with ZF2. The HTML code looks for eg as follows:
<fieldset id="benutzer">
<legend>Team</legend>
<div class="controls-row">
<span data-template="<input type="hidden" name="projekt[benutzer][__placeholder__][benutzerid]" class="benutzerid" value=""><input name="projekt[benutzer][__placeholder__][benutzername]" class="benutzername" title="Kollege" placeholder="Name" type="text" value=""><input type="hidden" name="projekt[benutzer][__placeholder__][bearbeiten]" value="0"><input type="checkbox" name="projekt[benutzer][__placeholder__][bearbeiten]" title="Bearbeiten" class="bearbeiten" value="1"><input name="projekt[benutzer][__placeholder__][bemerkung]" title="Bemerkung" placeholder="Bemerkung" type="text" value="">"></span>
<input type="hidden" value="4" class="benutzerid" name="todo[benutzer][0][benutzerid]">
<input type="text" value="Julian" placeholder="Name" title="Kollege" class="benutzername" name="todo[benutzer][0][benutzername]">
<input type="hidden" value="0" name="todo[benutzer][0][bearbeiten]">
<input class="bearbeiten" type="checkbox" checked="checked" value="1" title="Bearbeiten" name="todo[benutzer][0][bearbeiten]">
<input type="text" value="" placeholder="Bemerkung" title="Bemerkung" name="todo[benutzer][0][bemerkung]">
<div>
<div class="row">
<input type="hidden" value="5428" class="benutzerid" name="todo[benutzer][0][benutzerid]">
<input type="text" value="Hans" placeholder="Name" title="Kollege" class="benutzername" name="todo[benutzer][0][benutzername]">
<input type="hidden" value="0" name="todo[benutzer][0][bearbeiten]">
<input class="bearbeiten" type="checkbox" checked="checked" value="1" title="Bearbeiten" name="todo[benutzer][0][bearbeiten]">
<input type="text" value="" placeholder="Bemerkung" title="Bemerkung" name="todo[benutzer][0][bemerkung]">
<div>
</fieldset>
The user should now be able to check/uncheck the checkboxes, edit the input fields but also delete and/or add rows. The collections of ZF2 need the indexes of its rows to start at 0 and count up each row by 1. This means, if I delete a row and the row was not adventitiously at the end of the collection, I have to correct the indexes. I am trying to do this with jQuery.
And here is my Porblem: If I changed anything before I re-sort the rows (and I do this in the end everytime before I submit the form), the changes are not preserved. If I check a checkbox, if I enter something to the "Bemerkung"-field, everything will be discarded.
Here is my code:
Add a new row:
function add_category( $collectionType ) {
// Create and append new row
var $template = $('#'+$collectionType+' span').data('template');
var $currentCount = $('#'+$collectionType+' .'+$collectionType+'id').length;
$template = $template.replace(/__placeholder__/g, String($currentCount));
$newrow = "<div class=\"controls-row new-row\">"+$template+"<div class=\"span1\"><i class=\"icon-remove\"></i></div></div>";
$('#'+$collectionType).append($newrow);
return false;
}
Re-sort the collection:
$.fn.resortCollection = function (){ // execute with Collection-Fieldset!
$c = 0;
$fieldset = $(this);
// re-sort the rows, indexes
$.each( $fieldset.find('.controls-row'), function(){
$html = $(this).html();
$newhtml = $html.replace(/\[\d+\]/g, '['+$c+']');
$(this).html($newhtml);
$c++;
});
};
Delete a row in the collection:
$.fn.deleteRow = function (){
var $row = this.closest('div.controls-row');
var $fieldset = $row.closest('fieldset');
$($row).remove();
$fieldset.resortCollection();
};
How can I access the current state of the DOM including my changes while re-sorting the rows?
Actually Zend\Form\Element\Collection preserves the keys of a collection. The only problem that i found is that if you initially define collection with the option count > 0 e.g.:
$this->add(array(
'type' => 'Zend\Form\Element\Collection',
'name' => 'data',
'options' => array(
'count' => 2,
'allow_add' => true,
)
));
it will always add as much missing elements at the begining of the collection as you defined with count option. From the example above it will add elements with indexes 0 and 1 if they are missing in the collection.
I fixed that by extending Zend\Form\Element\Collection and rewriting one method:
namespace MyNamespace;
use Zend\Form\Element\Collection as ZendCollection;
use Zend\Form\Exception;
use Traversable;
class MyCollection extends ZendCollection
{
public function populateValues($data)
{
if (!is_array($data) && !$data instanceof Traversable) {
throw new Exception\InvalidArgumentException(sprintf(
'%s expects an array or Traversable set of data; received "%s"',
__METHOD__,
(is_object($data) ? get_class($data) : gettype($data))
));
}
if (sizeof($data)){
foreach ($this->byName as $name => $element) {
if (!isset($data[$name])) {
$this->remove($name);
}
}
}
parent::populateValues($data);
}
}
Hope it will help you to simplify your code.
Related
I'm using POST Method and I want the PHP script to return the data in JSON format
//data 1:
<input type="text" value="1" name="id[]">
<input type="text" value="aa" name="name[]">
<input type="text" value="cc" name="stuff[]">
//data 2:
<input type="text" value="2" name="id[]">
<input type="text" value="dd" name="name[]">
<input type="text" value="ff" name="stuff[]">
i want result be like :
{id:1,name:"aa",stuff:"cc"},{id:2,name:"dd",stuff:"ff"}
I understand that if we use json_encode($_POST,true) i will have :
{"id":["1","2"],"name":["aa","dd"],"stuff":["cc","ff"]}
i can do that with js using get method not post
id[]=1&name[]=aa&stuff=cc&id[]=2&name[]=dd&stuff[]=ff
Check my solution
https://jsfiddle.net/cqvny3th/
Or what if we generate url from the post method using http_build_query, result is :
id[]=1&id[]=2&name[]=aa&name[]=dd&stuff=cc&stuff[]=ff
But my solution works only with :
id[]=1&name[]=aa&stuff=cc&id[]=2&name[]=dd&stuff[]=ff
Regards
Definitely less elegant than #Don't Panic's solution, but in case you want/need to keep your name attributes as they are, this will work:
//prep
$repeated_post_vars = ['id', 'name', 'stuff'];
$arr = [];
//find which column has the most values, just in case they're not all equal
$num_items = max(array_map(function($col) {
return !empty($_POST[$col]) ? count($_POST[$col]) : 0;
}, $repeated_post_vars));
//iterate over value sets
for ($g=0; $g<$num_items; $g++) {
foreach($repeated_post_vars as $col)
$tmp[$col] = !empty($_POST[$col][$g]) ? $_POST[$col][$g] : null;
$arr[] = $tmp;
}
So if $_POST on submit looks like:
[
'id' => [1, 2],
'name' => ['foo', 'bar'],
'stuff' => [3]
];
The code produces:
[{"id":1,"name":"foo","stuff":3},{"id":2,"name":"bar","stuff":null}]
Rename your inputs, if you can.
<input type="text" value="1" name="data1[id]">
<input type="text" value="aa" name="data1[name]">
<input type="text" value="cc" name="data1[stuff]">
<input type="text" value="2" name="data2[id]">
<input type="text" value="dd" name="data2[name]">
<input type="text" value="ff" name="data2[stuff]">
That will group the data properly. Use array_values before json_encode so you'll get an array of objects rather than an object.
echo json_encode(array_values($_GET));
Could you do something like this?
$list = array();
for ($i=0; $i<count($_POST['id']); $i++) {
$item = new stdClass();
foreach ($_POST as $key => $values)
$item->{$key} = $values[$i];
$list[] = $item;
}
print json_encode( $list );
I'm working on a formular, but for the moment I just want to insert into an array my elements (I have books and authors).
I can display my books with author (name + surname) with the foreach, but I can't add more elements.
Here is the code with the form.
<H1>Exercice 2</H1>
<form method="POST">
<label for"code" >Number :</label>
<input id="code" name="code" type="number" />
<label for"title">Title :</label>
<input id="title" name="title" type="text" />
<label for"author" >Author :</label>
<input id="author" name="author" type="text" />
<button type="input" type="submit">Ok</button>
$title = $_POST['title'];
$code = $_POST['code'];
$author = $_POST['author'];
$book = array();
$book['code'] = 123;
$book['title'] = "Legendes";
$book['author'] = array("David", "Gemmel");
foreach($book as $value){
$book['key'] = $value;
var_dump($book);
if (is_array($value)) {
foreach($value as $otherValue) {
echo($otherValue);
}
} else {
echo($value);
}
}
I did some searcch, but I don't think it works, it's using the array_push() method with the POST, but I don't know where I can manipulate my form into the array.
If you want some details, I'll be happy to do that =) I'm working on it, if i have some news, you will know =)
Have a nice day =)
1) Assignments are in reverse. Correct way:
$myVar = $myValue
2) You need to set the name attribute in your inputs in order to be sent:
<input id="code" type="number" name="code" />
Then you can access them like:
$_POST['code']
3) To add an element by key in an array, use:
$array['key'] = $value;
Your Exercise 2 have some mistakes :
First, your HTML inputs must have the name attribute to be retrieved by post:
<h1>Exercice 2</h1>
<form method="post">
<label>
<input name="code" type="number" />
</label>
<button type="submit">Ok</button>
</form>
With PHP, you can access to any input value using the name:
$code = $_POST['code'];
Now, I think you want to "add" several books using this HTML form without a storage system. The problem is you can not do this if for every a new request since all the elements you have in your array will be deleted each time you run a new post request. To keep this information you need to use some persistent storage system as a database or others.
Since you seem to want to keep the information for each book together, you need to use a multidimensional array - hence, you'll need to redo the whole thing. Here's a suggestion:
Form:
<h2>Exercice 2</h2>
<form method="post">
<label for"code">Number :</label>
<input id="code" name="code" type="number">
<label for"title">Title :</label>
<input id="title" name="title" type="text">
<label for"author-firstname">Author First Name:</label>
<input id="author-firstname" name="author-firstname" type="text">
<label for "author-lastname">Author Last Name:</label>
<input id="author-lastname" name="author-lastname" type="text">
<input type="submit" name="submit_book" value="Ok">
</form>
Fixed the name-problems, changed the heading (you never, ever use H1 for a form, H1 is strictly used for the site-wide heading/logo/name of site). Also changed the button into a simple input type="submit".
$title = $_POST['title'];
$code = $_POST['code'];
$author = $_POST['author'];
$book = []; // changed this to modern PHP version array assignment
$book[0]['code'] = 123;
$book[0]['title'] = "Legendes";
$book[0]['author-firstname'] = "David";
$book[0]['author-lastname'] = "Gemmel"; // no reason to assign a separate array for first and last name, just use two array-keys
for ($c = 0; $c <= count($book); $c++) { //changed this to a for, counting the amount of entries in the $book array
echo 'Title: '.$book[$c]['title'];
echo 'Author: '.$book[$c]['author-firstname'].' '.$book[$c]['author-lastname'];
} // the content should probably be wrapped in a container of some sort, probably a <li> (and then a <ul>-list declared before the for-loop)
Now. None of this has anything to do with putting stuff INTO the array. That would be something like this (there isn't even a point of assigning the $_POST-variables for the code you posted. But, you can do something like this:
if (isset($_POST['submit_book'])) {
$title = $_POST['title'];
$code = $_POST['code'];
$author-firstname = $_POST['author-firstname'];
$author-lastname = $_POST['author-lastname'];
// however, if all you're doing is putting this into the array, no need to assigne the $_POST to variables, you can just do this:
$temp_array = ['code'=>$_POST['code'],'title'=>$_POST['title'],'author-firstname'=>$_POST['author-firstname'],'author-lastname'=>$_POST['author-lastname']];
$book[] = $temp_array;
}
So, that would replace the assigned variables at the beginning of your code.
I have three inputs type text in an HTML page and a button which if clicked duplicate each text box (Javascript) making them 6.
<input type="text" name="category[]">
<input type="text" name="quantity[]">
<input type="text" name="amount[]">
<button>Add more</button>
Which generate same inputs again:
<input type="text" name="category[]">
<input type="text" name="quantity[]">
<input type="text" name="amount[]">
A piece of code in Cakephp I have been trying:
$data = $this->request->data;
foreach($data['category'] as $index => $value){
$this->ModelName->save($value);
}
Trying to get two rows inserted at once with quantity, category and amount as columns. But it is not inserting and not giving any error.
Is there a way I can achieve this?
Thanks.
I'm not sure how your model works in cakephp, but you should be able to get a complete grouping of data like:
foreach($data['category'] as $index => $value){
$category = $value
$quantity = $data['quantity'][$index];
$amount = $data['amount'][$index];
// use the above 3 variables however you need to to persist the model
//$this->ModelName->save($value);
}
On a side note, you may want to consider reordering your html inputs to be like:
<input type="text" name="item[0][category]">
<input type="text" name="item[0][quantity]">
<input type="text" name="item[0][amount]">
And then maintain the next index, incrementing the numeric index of item for each additional group
This will allow you to iterate like:
foreach($data['item'] as $index => $group){
//$group['category'];
//$group['quantity'];
//$group['amount'];
}
I'm working on a CMS in Codeigniter and one main part is a form for creating and editing posts.
I've been planning on using the same view file for both since all of the elements are shared. The only difference would be the form is blank when creating and it's populated when being edited. Is this the right way to go?
I was thinking about having a method for each, so post/create and post/edit($id).
In the create method in the post controller I have all the form data like this (for errors):
$this->data['item_title'] = array(
'name' => 'item_title',
'id' => 'item_title',
'type' => 'text',
'value' => $this->form_validation->set_value('item_title'),
);
I'm thinking about just altering the value to hold the database value instead of set_value(), so something like:
public function edit($id) {
$post_data = $this->post_model->get_post_data($id)
$this->data['item_title'] = array(
'name' => 'item_title',
'id' => 'item_title',
'type' => 'text',
'value' => $post_data['post_title'],
);
}
Am I on the right track or is there a better way to approach this? Should I just use 2 views?
i use a partial _form.php that is shared by a new and edit controller action. on both actions i have the same validations so i moved those to the controller constructor, then for each input i just use a ternary operator that says if the existing value $title is provided then populate the <input> value using it, otherwise use the codeigniter set_value() helper to populate with the validation value.
<input type="text" name="title" value="<?php echo isset($title) ? set_value("title", $title) : set_value("title"); ?>" />
I usually use one view with a few variables in it. The values of the fields can either be set from the data from the server or they can be left blank. Depending on whether data is being provided or not I change which action the form will use because it may be adding or editing.
This should be the most efficient method since it uses the idea of reusability :)
A quick example
<form action="<?php echo !$data ? "admin/add" : "admin/edit" ?> method="post">
<input type="text name="test" value="<?php echo $data['test'] ? $data['test'] : "" ?>" />
</form>
I'm not pro at CodeIgniter (much better at CakePHP) but in the heart of MVC is that one action has one view.
You have no reason to put it in one view. :)
It's certainly possible, as I do it all of the time.
Normally, I would have:
Action
function edit($PageID = -1)
{
$Page = new stdClass();
if($PageID === -1)
{
$Page->Title = $Page->Description = $Page->Keywords = '';
$Page->PageID = -1;
}
else
{
$this->load->model('page_model');
$Page = $this->page_model->GetByPageID($PageID);
if(empty($Page))
{
show_404();
return;
}
}
if($this->input->post('Save', true) !== false)
{
// perform validation
if($PageID === -1)
{
// insert
}
else
{
// update
}
}
$data = array
(
'Page' => $Page
);
$this->load->view('edit_page', $data);
}
View
<?= form_open(); ?>
<fieldset>
<label for="title">Title: </label>
<input type="text" name="title" id="title" value="<?= Form::Get('title', $Page->Title); ?>" />
<br />
<label for="description">Description: </label>
<input type="text" name="description" id="description" value="<?= Form::Get('description', $Page->Description); ?>" />
<br />
<label for="keywords">Keywords: </label>
<input type="text" name="keywords" id="keywords" value="<?= Form::Get('keywords', $Page->Keywords); ?>" />
<br />
<input type="submit" name="Save" value="Save" />
</fieldset>
</form>
Edit
Sorry, I should have mentioned, Form::Get is not a CodeIgniter function, but one I have created. Simply, it takes the path to the Post value you need to read. If it doesn't exist, i.e. you haven't posted, then it will simply display the value from the second parameter.
If I can dig the code out for you, I will post it.
I have a pages with multiple activeCheckBoxLists which are actually generated in a foreach loop. The issue that I am having is that each list is generated with the same name's and id's as every other generated list. I need a way to either send an iteration or something into the id's and name's so that they are unique.
When I use jquery to select the first item of the list it selects the first item of every list.
Here is the code that generates the lists.
// collect all filter titles (level == 0, parent == 0)
$topLevelFilterTitles = $hsf->findAllByAttributes(array('level'=>'0','parent_id'=>'0'));
foreach($topLevelFilterTitles as $filterTitle):
// with each filter title find all children (level == 1, parent == filter title id)
echo "<div class='half menu split'>";
echo "<p class='uppercase-text filter-name'>" . $filterTitle->title . "</p>";
$filterOptions = $hsf->findAllByAttributes(
array(
'level'=>'1',
'parent_id'=>$filterTitle->id,
)
);
$list = CHtml::listData($filterOptions,'filter_name','title');
echo CHtml::activeCheckBoxList(
$hsf, // model
'filter_name',
$list
);
echo "</div>";
endforeach;
The generated lists show up as follows
<div class="halfmenu split">
<p class="uppercase-text filter-name">Filter Name R</p>
<input id="ytHardwareSearchFiltering_filter_name" type="hidden" value="" name="HardwareSearchFiltering[filter_name]" />
<input id="HardwareSearchFiltering_filter_name_0" value="r_value0" type="checkbox" name="HardwareSearchFiltering[filter_name][]" />
<label for="HardwareSearchFiltering_filter_name_0">r_name0</label><br/>
<input id="HardwareSearchFiltering_filter_name_1" value="r_value1" type="checkbox" name="HardwareSearchFiltering[filter_name][]" />
<label for="HardwareSearchFiltering_filter_name_1">r_name1</label>
</div>
<div class="halfmenu split">
<p class="uppercase-text filter-name">Filter Name T</p>
<input id="ytHardwareSearchFiltering_filter_name" type="hidden" value="" name="HardwareSearchFiltering[filter_name]" />
<input id="HardwareSearchFiltering_filter_name_0" value="t_value0" type="checkbox" name="HardwareSearchFiltering[filter_name][]" />
<label for="HardwareSearchFiltering_filter_name_0">t_name0</label><br/>
<input id="HardwareSearchFiltering_filter_name_1" value="t_value1" type="checkbox" name="HardwareSearchFiltering[filter_name][]" />
<label for="HardwareSearchFiltering_filter_name_1">t_name1</label><br/>
</div>
I need (preferably) the ID to be different between lists, but even if I could get the name different that would be a start. Thanks in advance for the help.
You can add htmloptions to the activeCheckBoxList as indicated in the API documentation. In your case I reckon you could do something like this:
echo CHtml::activeCheckBoxList(
$hsf, // model
'filter_name',
$list, array(
// Here you could use your filterTitle id or any other
// variable that will make your checkbox unique
'id' => 'chk_'.$filterTitle->id,
'name' => 'chk_'.$filterTitle->id
)
);
The Htmloptions are the 4th parameter in the instantiation of activeCheckBoxList as an array. Look at the API!