How to store data from dynamically added lines with multiple inputs - php

I created a form containing info about the user (doctors). Besides the default info, they now want to add opening hours from their office.
In the current form I added a form-group with 3 input elements:
select name="dayofweek[]"
input name="timeblock[]"
select name="type[]"
There is a button for the user to add an extra line with the same fields so he can create multiple opening hours (per day/timeblock).
All is stored with the submit-button from the form.
After saving my data looks like this:
"dayofweek" => ["monday", "monday", "tuesday", ...]
"timeblock" => ["10-12h", "14-18h", "10-16u", ...]
"type" => ["by appointment", "free", "free", ...]
Now I want to save these in my table with fields [ID, DayOfWeek, TimeBlock, Type].
To do so I have to rearrange the data received from the form-submit before I can save them row by row. This is how I do it now:
public static function prepareData($dayofweek = [], $timeblock = [], $type = []) {
$prepared = [];
$i = 0;
while($i < count($dayofweek)) {
$a = $dayofweek[$i];
$b = $timeblock[$i];
$c = $type[$i];
$record = ['dayofweek' => $a, 'timeblock' => $b, 'type' => $c];
array_push($prepared, $record);
$i++;
}
return $prepared;
}
To show the data on the form when editing, I have to do the inverse.
I was wondering if there is any other easier and cleaner way to do this?

Unfortunately, the native data type like array and string can have only one format and structure. If your use case uses the same data in different formats or different data structure, it is recommended to create a data object. A data object is an object that holds the data in its fields and has many inputs and output methods to allow to use the data in different formats and structures.
This is an OOP approach, in this case, it will make your code much cleaner and understandable and much more easy to expand or alter in the future. But note that this will not reduce the amount of code. The format conversion function is required anyway.
According to the description, we have a data object named Appointment with the data {DayOfWeek, TimeBlock, Type}. However, the input and output functions that are described referring to a list of Appointments, therefore, those functions do not belong to Appointment object. They refer to another data object, AppointmentList which contain an array of Appointments and input and output functions.
This object will look like this:
class Appointment
{
public $dayofweek;
public $timeblock;
public $type;
public function __construct($record)
{
$this->dayofweek = $record['dayofweek'];
$this->timeblock = $record['timeblock'];
$this->type = $record['type'];
}
}
class AppointmentList
{
public $appointmentArray = [];
function setData($data)
{
$prepared = [];
$i = 0;
while ($i < count($data['dayofweek'])) {
$a = $data['dayofweek'][$i];
$b = $data['timeblock'][$i];
$c = $data['type'][$i];
$record = ['dayofweek' => $a, 'timeblock' => $b, 'type' => $c];
$prepared[] = new Appointment($record);
$i++;
}
$this->appointmentArray = $prepared;
}
function getData() {
$data = ['dayofweek' => [],'timeblock' => [],'type' => []];
foreach ($this->appointmentArray as $appointment){
$data['dayofweek'][] = $appointment->dayofweek;
$data['timeblock'][] = $appointment->timeblock;
$data['type'][] = $appointment->type;
}
return $data;
}
}
Then when you receive $data from the form run:
$al = new AppointmentList();
$al->setData($data);
The you can use the array $al->appointmentArray to access the appointments one by one and store them in the table.
And afterward when you need to fill the form again simply use $al->getData()
Note that this is only an example. Usually different techniques are used to store the data object into the table automatically.
Hope this helps. Good luck.

Related

How to make update endpoint more general purpose? (PHP)

I have a function in my API to update the name of a person in an SQLite database. You give it the ID of the name you wish to change and the new name.
How can I build a function in a way that allows me to update a wide range of fields in the database? even things from different tables?
I started off trying to use parameters to switch which SQL query is executed, but this feels a bit clunky and not scalable. Is there a better way?
Current code:
private function json_update_authors() {
$input = json_decode(file_get_contents("php://input"));
$query = "UPDATE authors SET name = :name WHERE authorId = :authorId";
$params = ["name" => $input->name, "authorId" => $input->authorId];
$res = $this->recordset->getJSONRecordSet($query, $params);
return json_encode(array("status" => 200, "message" => "ok"));
}
Prependix
You can achieve what you want, but before reading the details, I recommend contemplating about what you would like to restrict this to, because if there is a file your function blindly trusts, then, should malicious input be inside that file, your database can easily be hacked. So, you should have a whitelist of tables/fields that you allow to be updated and apply that.
Decoding JSON
json_decode decodes your JSON into an object that you do not foresee its members. However, according to the documentation you can iterate this object like:
foreach ($obj as $key => $value) {
echo "$key => $value\n";
}
However, json_decode can decode your JSON into an array as well, like:
$input = json_decode(file_get_contents("php://input"), true);
I personally prefer to decode JSON into arrays, but you can operate with the first approach as well. In both cases, you can iterate the array in a similar manner as described above.
Recommended format
Your update has an anatomy as follows:
table
fields
filter
So, I would recommend that you could use a JSON representation of your input, that has a tableName field, which is a string, a fields field, which is an array of key-value pairs, the keys representing the fields to be updated and the values representing the values to update to and finally a filter field, which, if we intend to be very elegant, could also be an array of objects of key-value pairs, the keys representing the fields you are to filter by and the values representing the values you would filter with. A sample Javascript object that would comply to this format would look like the following:
[
{ //Your query
tableName: 'authors',
fields:
[
{
name: 'somename'
}
],
filters:
[
{
authorId: 123
}
]
},
{ //Some other example
tableName: 'books',
fields:
[
{
isbn: 'someisbn',
title: 'sometitle'
}
],
filters:
[
{
pageNumber: 123,
weight: '5kg'
}
]
},
]
I have given an example above, of two objects, so you can see that:
several updates can be notified in the JSON
you can update several fields in a single command
you can filter by several fields
I should mention that this is a rabbit hole, because you might want to vary the operator as well, but since this is a mere answer, I do not write a full elegant project for its purpose. Instead of that, let me just tell you that there is a lot of room for improvement, operator dynamicity springs to mind instantly as an improvement that you may need.
How to generate an update query:
//assuming that $JSON is a variable holding such values as describe in the previous chapter
foreach ($JSON as $obj) {
$tableName = $obj['tableName'];
$fields = [];
$filters = [];
$params = [];
$toExecute = isset($whiteList['tables'][$tableName]);
foreach ($obj['fields'] as $key => $value) {
$fields[]=($key.'=:field_value'.$key);
$params['field_value'.$key] = $value;
$toExecute = $toExecute && isset($whiteList['fields'][$key]);
}
foreach ($obj['filters'] as $key => $value) {
$filters[]=($key.'=:filter_value'.$key);
$params['filter_value'.$key] = $value;
$toExecute = $toExecute && isset($whiteList['filters'][$key]);
}
}
I have used a whitelist above to make sure that the queries will not update tables/fields using filters where the name of the table/field/filter is either badly formatted, malicious or unwanted. This code is untested, it might well contain typos, but the idea should be a good starting point.

insert the last inserted record id into an array in another table using Codeigniter

so I got two tables one called patients and another called tests , the tests table has the patient id , i have a page called add patient which have fields for adding new patient information and other fields for adding their test info and upload all the data into the two tables in one query ,tests fields could be duplicated by ajax to i could add more than one test to the same patient , now i wanna add more than one test at the same time into the tests table and i managed to do so but the case is i can't add the patient_id into tests table, i wanna add the same patient_id more than one time to all the tests i have added while adding a new patient in that page, i'm new to Codeigniter!
this is the adding page
the patient fields and the test fields
<input type="text" name="patientname" />
<input type="text" name="address" />
<input type="text" name="testname[]"/>
<input type="text" name="price[]" />
and this is my controller
public function testbby
{
$this->load->model("khmodel", "Khmodel");
// patient main info
$patient_input_data = array();
$patient_input_data['patientname'] = $this->input->post('patientname');
$patient_input_data['address'] = $this->input->post('address');
//test data
$testname = $this->input->post('testname[]');
$price = $this->input->post('price[]');
$test_input_data = array();
for ($i = 0; $i < count($testname); $i ++ )
{
$test_input_data[$i] = array(
'testname' => $testname[$i],
'price' => $price[$i],
);
}
$this->Khmodel->insert_bby($patient_input_data, $test_input_data);
redirect('main/dashboard');
}
and this is my model
public function insert_bby($patient, $test)
{
$this->db->insert('patients', $patient);
$patient_id = $this->db->insert_id();
// i used this and it worked but only while adding one test , s
//once it's gonna be an array i dunno what to do with the id !
//$test['refpatient']=$patient_id;
$this->db->insert_batch('tests', $test);
return $insert_id = $this->db->insert_id();
}
To start with, you don't need this.
$patient_input_data = array();
Because when you make the call
$patient_input_data['patientname'] = $this->input->post('patientname');
The array, $patient_input_data, will be created. There are times when you might want to make sure you have an array even if it's empty. But this isn't one of them.
For the array input values, ie testname[], get the data by leaving off the brackets at the end of the name. Like this.
//test data
$testname = $this->input->post('testname'); //instead of post('testname[]')
$price = $this->input->post('price');
The vars $testname and $price will be arrays with an item for each field on the form.
Seems to me that those two inputs are required so you should add code to check that is the case. The Form Validation class is excellent for that purpose.
The array $test_input_data is a case where you will want the array to exist - even if it's empty. You don't have to explicitly set the index value when adding items to the array, i.e. $test_input_data[$i] = array(... because $test_input_data[] = array(... will work just fine, but there's no harm either way.
On to the model. The first part is good. For the second you need to create arrays that include the patient id you got from the first insert and add that value to each of the sub-arrays in the $tests argument. The model then becomes this.
public function insert_bby($patient, $tests)
{
$this->db->insert('patients', $patient);
$patient_id = $this->db->insert_id();
// add the patient id key/value to each sub-array in $tests
foreach ($tests as $test)
{
$test['patient id'] = $patient_id;
}
// will return the number of rows inserted or FALSE on failure
return $this->db->insert_batch('tests', $tests);
}
the value i mean , i dont know but your code seems to be so right and logical
but i have tried this code and it worked so well , i didn't even use the model/
public function testbby
{
$this->load->model("khmodel", "Khmodel");
// patient main info
$patient_input_data = array();
$patient_input_data['patientname'] = $this->input->post('patientname');
$patient_input_data['address'] = $this->input->post('address');
//test data
$testname = $this->input->post('testname[]');
$price = $this->input->post('price[]');
$this->db->reset_query();
$this->db->insert('patients', $patient_input_data);
$patient_id=$this->db->insert_id();
$test_input_data = array();
for ($i = 0; $i < count($testname); $i ++ )
{
$test_input_data[] = array(
'testname' => $testname[$i],
'price' => $price[$i],
'patient_id'=>$patient_id
);
}
$this->db->reset_query();
$this->db->insert_batch('tbl_tests',$test_input_data);
redirect('main/dashboard');
}

Save _POST values to an array

I have an array of the names of my POST variables to use when I update a row in my database.
$jobs = array( "proposal_id",
"will_provide",
"general_scope",
"per_bid",
"job_type");
Using this style my table is called jobs and each value in the array is a column id.
I want to edit this array so each item (column id) contains a single _POST Value
Then I have a function that uses the variables to create generic queries.
function save_data($jobs) {
foreach ($jobs as $job)
{
$job[$job[$i]] = _$Post[$job];
or
Table_name[column] = cell value;
...
...
...
I would like to be able to save $values into the post variables associated to it. Something like
For example if I was going to manually create this array it would look like
$jobs = array('proposal_id' => '12345678','title_of_project' => 'aTitle','creator' => 'aUser','last_modified' => '0000-00-00','date_created' => '0000-00-00','price' =>'1000');
This should be what you're looking for:
$jobs = array( "proposal_id",
"will_provide",
"general_scope",
"per_bid",
"job_type");
$jobValues = array();
foreach($jobs as $job) {
$jobValues[] = isset($_POST[$job]) ? $_POST[$job] : null;
}
$jobs = array_combine($jobs, $jobValues);

A more efficient way of saving 125 columns in a Laravel controller

I have a rather large table that has 125 data inputs, each of which has to be saved in a separate column.
I have named each HTML input as 1,2,3 etc... and the same within the table, to hopefully help things.
At the moment, I have the following code:
$observation = new Observation();
$observation->site_id = Input::get('site_id');
$observation->result = Input::get('1');
$observation->result_id = '1';
$observation->save();
Is there a way I could use a loop to iterate through the 125 data inputs (Input::get('X') and result_id = 'X') and then save them all?
for($i=1; $i<=125; $i++) {
$data[$i] = [
'site_id'=>Input::get('site_id'),
'result'=>Input::get($i), 'result_id'=>$i
];
}
Using Query builder:
DB::table('table')->insert($data); // Change this with your table name.
and if you want to use Eloquent:
Model::insert($data);
You can use something like this pattern in your controller to handle creating new Observations and editing existing Observations without worrying about the specific attribute names:
// get all non-empty inputs except 'token'
$inputs = array_filter(Input::except('_token'), 'strlen');
// assuming 'id' is your primary key and sent as an input in your form
if (Input::has('id'))
{
// An edit
$id = Input::get('id');
$observation = Observation::find($id);
foreach ($inputs as $key => $value)
{
$observation->$key = $value;
}
}
else
{
// A create
$observation = new Observation($inputs);
}
This solution doesn't force you to use sequential column names or html input names like 1..125 and will allow you to use more meaningful column names if you prefer. However, it does assume that your column names (and therefore object attributes) are the same as the html input names.
Related, but you might also like to know that if you use the HTML helpers in the Blade view template to construct your form, and open the form with Form::model, it will fill even fill in the values of the inputs using the object that you pass to it.
For example, in the view:
{{ Form::model($observation) }}

post an array and iterate throught it in PHP codeigniter

This is the first time i create my own webservice (someone always did it for me before), so please bear with me.
I post this array :
$data = array(
'user_id' => $this->post('user_id'),
'group_id' => $this->post('group_id'),
'child_id' => $this->post('child_id'), //will be nested array
'custom' => $this->post('custom'),
'time' => $this->post('time'),
'date' => $this->post('date')
);
I tried to create a nested array with this : $this->post('child_id'), because user can post multiple child_id at once.
Then i tried to iterate through the child_id, because i need to insert them to the mysql :
for($i = 0; $i < sizeof($data['child_id']); $i++)
{
$result2 = $this->schedule_m->add_trans('transaction_schedule', $data, $result_id[0]['id']);
}
What should i do, so i can have an array of child_id in my $data array? (nested array)
And how to iterate through it?
UPDATE :
I have updated the codes above.
I use advanced rest client for testing, and i tried to post something like this in the form content type :
child_id=1&user_id=1&group_id=1&custom=&time=17%3A17%3A00&date=&child_id=2
Notice that theres two child_id (left most and right most), but only the last one (right most) is inserted.
And this is the add_trans in the model :
function add_trans($table, $data, $schedule_id) {
$query = $this->db->insert($table, array('child_id' => $data['child_id'], 'schedule_id' => $schedule_id));
return $query;
}
Thanks a lot for your time.
Even thought you set the name attribute as child[] on the markup,
You still need to call it as:
'child_id' => $this->post('child_id')
It will still return an array.
for($i = 0; $i < sizeof($data['child_id']); $i++) {
$result2 = $this->schedule_m->add_trans('transaction_schedule', $data, $result_id[0]['id']);
}
EDIT:
Looking upon you query string, that seems to be the culprit:
child_id=1&user_id=1&group_id=1&custom=&time=17%3A17%3A00&date=&child_id=2
^ same index , same index, same index, it will overwrite and you will get only `2`
If you want to get them all into an array format, you need to set them like this
child_id[]=1&user_id=1&group_id=1&custom=&time=17%3A17%3A00&date=&child_id[]=2
^ it needs to be set like this
UPDATE:
And in your model, if you want each id per row, well you can also loop in this case:
function add_trans($table, $data, $schedule_id) {
foreach($data['child_id'] as $child_id) {
$query = $this->db->insert($table, array('child_id' => $child_id, 'schedule_id' => $schedule_id));
}
// return $this->db->insert_id();
return $query;
}
ofcourse that won't work, it has to be
for($i = 0; $i < sizeof($data['child_id']); $i++)
{
$result2 = $this->schedule_m->add_trans('transaction_schedule', $data['child_id'][$i], $result_id[0]['id']);
}
because you've not set $data['child_id[]'] so it doesn't exist, the key is just a string or number, it does not validate or parse anything
you don't need to give child[] in post method. just give only child, it will get complete array what are you sending from views
replace
'child_id' => $this->post('child_id[]')
with
'child_id' => $this->post('child_id')

Categories