I have a php class
<?php
class Students
{
public $sesscode;
public $db_data;
function __construct($sesscode)
{
$this->sesscode = $sesscode;
$this->db_data = dbfetch(null, "SELECT * FROM students WHERE code = ?", [$this->sesscode]); //this simply returns data from db
$this->db_data = (empty($this->db_data)) ? $this->createProfile() : $this->db_data[0];
}
public function createProfile()
{
dbquery(null, "INSERT INTO students (code) VALUES (?)", [$this->sesscode]);
return dbfetch(null, "SELECT * FROM students WHERE code = ?", [$this->sesscode])[0];
}
public function updateAccount($row, $key, $val)
{
$data = json_decode($this->db_data[$row], true);
if (!$data) {
$data = [
$key => $val,
];
} else {
$data[$key] = trim($val);
}
dbquery(null, "UPDATE students SET account = ? WHERE code = ?", [json_encode($data), $this->sesscode]);
}
public function getData($row)
{
return json_decode($this->db_data[$row], true);
}
}
?>
Usage:
$student = new Student($sesscode); //sesscode is just some string
if (!empty($_POST)){
$student->updateAccount("account", "course", $_POST['course']);
//see class code/updateAccount method above for parameters. This means update dB table row "account", decode json and set course to post value of course
$student->updateAccount("account", "email", $_POST['email']);
$student->updateAccount("account", "phone", $_POST['phone']);
$student->updateAccount("account", "institution", $_POST['institution']);
}
My problem is, only the last line executes (and no, it isn't overwriting the data)
Moving the line that sets phone to take the place of institution will set phone and ignore those above it.
The line
$data = json_decode($this->db_data[$row], true);
resets the value of $data every time your updateAccount account function runs. It resets it to the value you fetched from the database when you created the object. Therefore this doesn't take account of any changes you've made in previous calls to updateAccount. So in fact it does overwrite your data, but probably just quite not in the way you imagined.
You need to either:
a) keep $data in-memory as a (private) property of the class rather than resetting it each time updateAccount runs,
or
b) re-fetch the existing data from the database each time updateAccount runs,
or
c) Store your data in relational format (with fields in separate columns) instead of as JSON within a single column - meaning you could update individual fields without interfering with the others,
and/or
d) provide a version of updateAccount where multiple values can be updated simultaneously - running separate UPDATE queries for each field is very inefficient.
Related
I am working on an android app which uses APIs made with php. Here, i am dynamically creating columns and their values.
I am verifying the API via postman and a strange thing happens every time, While looping through the Json Object what i am doing is first creating column and then inserting its values.
The problem is only the 1st iteration saves the element and rest of them only creates the column but does not insert the values. I don't know if i am doing anything wrong, below is my php code.
<?php
include("connection.php");
$data = file_get_contents('php://input');
$json_data = json_decode($data);
foreach($json_data as $key => $val) {
$column_name = $key ;
$c_column_name = preg_replace('/[^a-zA-Z]+/', '', $column_name);
$column_value = $val ;
$table_name = "test2";
$email = "ht#t.com";
$result = mysqli_query($conn,"SHOW COLUMNS FROM $table_name LIKE '$c_column_name'");
$exists = (mysqli_num_rows($result))?TRUE:FALSE;
if($exists) {
$query1 = "INSERT INTO $table_name($c_column_name)VALUES('$column_value') ";
$data0=mysqli_query($conn,$query);
if($data0)
{
echo json_encode(array("success"=>"true - insertion","message"=>"Column existed, Successfully data sent."));
}
else{
echo json_encode(array("success"=>"false - insertion","message"=>"Column existed, data not inserted."));
}
}
else{
$query2="ALTER TABLE $table_name ADD COLUMN `$c_column_name` varchar(50) NOT NULL";
$data1=mysqli_query($conn,$query2);
if($data1){
$query3="INSERT INTO $table_name($c_column_name)VALUES('$column_value')";
$data2=mysqli_query($conn,$query3);
if($data2)
{
echo json_encode(array("success"=>"true - insertion","message"=>"Successfully data sent."));
}
else{
echo json_encode(array("success"=>"false - insertion","message"=>"Column created but data not inserted."));
}
}
else
{
echo json_encode(array("success"=>"false - column creation","message"=>"Failed to create column.'$column_name', '$table_name', '$conn'"));
}
}
}
?>
Here is the Json Object through postman.
{"Shape":"rewq","Trans.No.":"yuuiop","Color":"qwert"}
Please help me with this, any help or suggestions are highly appreciated.
The second column name is Trans.No. which contains a dot, this is why it fails, probably you have an error as a result which prevents further columns from being created.
I think it would be much better to have a table with this structure:
attributes(id, key, value)
and whenever a key-value pair is received, you just insert/update it, depending on the logic you need to be executed. Your current model will create a separate row for each attribute, which is probably not what you want to achieve.
EDIT
Based on the information received in the comment section I reached the following conclusion:
You could create the missing columns first and then generate the insert statement with all the columns, having a single insert.
But it would be better to not create a separate column for each value, as the number of columns could quickly get out of hand. Instead you could have a table:
myentity(id, name)
for storing the entities represented by the JSON and
attributes(id, myentity_id, key, value)
for storing its attributes. This would be a neat schema with all the dinamicity you could want.
I am making a transaction table with an auto increment field of BIGINT(20).
When a new transaction is added, the insert Id is retrieved and formatted to be more readable:
public function add_transaction($paymethod, $cursus_id)
{
$this->load->model('Config_model');
$btw = $this->Config_model->get('transactions.btw');
$query = " INSERT INTO transacties(userid, paymethod, amount, btw_pc)
VALUES((SELECT userid FROM users WHERE lcase(username)=lcase('{$this->session->userdata('username')}')),
'{$paymethod}',
(SELECT prijs FROM p_cursus_uitvoering WHERE uitvoering_id = {$cursus_id}),
{$btw});";
$this->db->query($query);
$insertId = $this->db->insert_id();
$newCode = date('Ymd') . str_pad($insertId, 8, '0', STR_PAD_LEFT);
$this->db->where('transact_id', $insertId);
$this->db->update('transacties', ['transact_id' => $newCode]);
return $newCode;
}
The result is that the ID get updated from eg: 5 to 2015041800000005.
This is working perfectly, but as you can see, the newCode is returned in the function and used by another function where it's reinserted in another table.
Here is where the problem arises, the ID turns into: 201504192058506757.
Even when I echo the newCode, it still prints 201504192058506757.. even though it is inserted correctly once, but incorrectly the second time!
EDIT:
Here is the code snippet in which the function is called:
public function workshop(){
$this->load->model('Inschrijven_model');
$paymethod = $this->input->post("paymethod");
//eigenlijk uitvoering_id.....
$cursus_id = $this->input->post("cursus_id");
if($paymethod == null || $cursus_id == null){
redirect('cursus');
}
if($this->Inschrijven_model->cursus_has_room($cursus_id)){
if(!$this->Inschrijven_model->cursus_ingeschreven($cursus_id)){
$this->load->model('Cc_payment_model');
$this->load->model('Cursus_model');
$amount = $this->Cursus_model->get_price($cursus_id);
$orderId = $this->Cc_payment_model->add_transaction($paymethod, $cursus_id);
$this->Inschrijven_model->cursus_inschrijven($cursus_id,$orderId);
$function = explode('_',$paymethod);
$this->{$function[0]}($function[1], $amount, $orderId);
}else{
echo "Al ingeschreven";
}
}else{
echo "geen ruimte";
}
}
And here is the code snippet in which the return $newCode is being reinserted:
public function cursus_inschrijven($cursus_id,$transaction_id){
$query = " INSERT INTO p_cursus_in(uitvoering_id, userid, transact_id)
VALUES({$cursus_id},(SELECT userid FROM users WHERE lcase(username)=lcase('{$this->session->userdata('username')}')),{$transaction_id})";
$this->db->query($query);
}
It is reinserted so I can make a connection between someone's registration into a class and their payment for that class.
You have to convert the BIGINT to string before you get it into PHP
Here's how you can convert it with a SQL statement:
https://dev.mysql.com/doc/refman/5.7/en/cast-functions.html#function_convert
I don't know what framework you're using but you have to do a conversion if you're using BIGINT whose length is not supported by PHP directly. It seems that the framework will read the record after updated to check if the operation's done successfully, and because of the process of reading without the conversion, you got a wrong number.
You can also use PHP GMP module to handle BIGINT:
https://php.net/manual/en/book.gmp.php
I'm looking for a way to make MySQL insert/update queries more dynamic and fast to code since sometimes one just need another field in a form (when for example prototyping an application). This might be a dumb question.
My idea is to make an insert or update if ids match, and if table/fields doesn't exists create it with one function dynamically.
<?php
// $l is set with some db-login stuff
// creates and inserts
$f[] = nf(1,'this_id_x'); // this_id_* could be a prefix for ids
$f[] = nf('value yep',$fieldname_is_this2)
$tbl_name = "it_didnt_exist";
nyakilian_fiq($l, $tbl_name, $f);
// Done!
//This would do an update on above
$fieldname_is_this2 = "this is now updated";
$f[] = nf(1,'this_id_x');
$f[] = nf($fieldname_is_this2); // the function takes the variable name as field name
$tbl_name = "it_didnt_exist";
nyakilian_fiq($l, $tbl_name, $f);
?>
I have been using this function with success. It doesn't add a column but that is against the structure of my MVC framework. Try something like this:
public function save(DatabaseConnection &$db)
{
$properties = get_object_vars($this);
$table = $this->getTableName();
// $cols = array();
// $values = array();
foreach ($properties as $key => $value) {
$cols[] = "`$key`";
$values[] = '"'.$value.'"';
if ($value != NULL) {
$updateCols[] = "`$key`".' = "'.$value.'"';
}
}
$sql = 'INSERT INTO '.$table.' ('.implode(", ", $cols).') VALUES ('.implode(", ", $values).') ON DUPLICATE KEY UPDATE '.implode(", ", $updateCols);
$stmnt = $db->prepare($sql);
var_dump($stmnt);
if ($stmnt->execute($values)) return true;
return false;
}
I have a model abstract class that I extend with a child class for each database table. This function sits in the model abstract. Each child class contains a public property [so I can use PDO::fetchObject()] that corresponds to a column name in the table. If I need to create the table on the fly, I add a function to the child class to do so.
This is quite unusable approach.
You are trying to mix into one single function (not even a class(!) a functionality that fits for a decent framework. That's just impossible (or unusable for some parts).
Yet it resembles major frameworks' Models in many aspects.
So, I could give just some recommendations
Do not create tables dynamically. Data structure is a backbone of the application and have to be solid.
do not take too much considerations (like "If an ID is passed"). it will tie your hands for whatever more complex case
take a look at some major frameworks - it seems your wishes are already fulfilled with their codegeneration feature (an ugliest thing that ever existed on the Erath in my private opinion). They're working pretty the same way you're talking about: you have to only define a Model and the rest is done by framework's methods
I have the function getDistance(). The function findDistance() inside the while loop, calculates the distance between 2 users, by using coordinates (latitude-longitude), and returns to var $djson the distance in meters. $distance is a string committed by the user for first time and $user_old["distance"] is a string which is called from a database in $query. I wanted to be able in $matched_names, to save all the names of the users from my database, for who the condition inside if() is true, regarding the sum of the distance of the new user who commits his data and the old ones inside the database. The problem is that $matched_names saves the first name which is called from the database and for as many times the loop goes on without even considering the if() restriction. For example if the first name called in $user is "Mike", and $user has 5 rows then the output will be: Mike,Mike,Mike,Mike,Mike.
I suppose that i have made some mistake in the way things work inside while..
<?php
public function getDistance($uuid, $name, $distance, $latstart, $lonstart, $latend, $lonend, $gcm_regId) {
$query = sprintf("SELECT uid, gcm_regid, name, distance,latstart, lonstart, latend, lonend FROM user_demand WHERE latstart='%s' AND lonstart='%s'",
mysql_real_escape_string($latstart),
mysql_real_escape_string($lonstart));
$user = mysql_query($query);
$no_of_rows = mysql_num_rows($user);
$user_old = mysql_fetch_assoc($user);
while( $user_old = mysql_fetch_assoc($user)) {
$djson = $this->findDistance($latend,$lonend,$user_old["latend"],$user_old["lonend"] );
if ($user_old["distance"] + $distance >= $djson) {
$match = $this ->df->addUserMatch($user_old['gcm_regid'],$user_old['name'],$gcm_regId,$name);
$matched_names = array_fill(0,$no_of_rows,$user_old['name']);
$matched_gcmz = array_fill(0,$no_of_rows,$user_old['gcm_regid']);
}
}
$registatoin_ids = array($gcm_regId);
$message = array("names" =>$matched_names,"gcm" => $matched_gcmz);
$result = $this ->gcm->send_notification($registatoin_ids, $message);
}
?>
What i usually do is, when I'm going to write something that is complicated, is to get it to working without being in a function then break it down into functions.
It is less confusing and easier to troubleshoot problems that way.
It is kind of hard to tell what it is doing since you didn't post your other function and the if statement relies on the output of that function.
Offhand, it could be this line where you are using the $name and $user_old['name']
$match = $this ->df->addUserMatch($user_old['gcm_regid'],$user_old['name'],$gcm_regId,$name);
I think you would want it to match each other. Like if $name = $user_old['name'] then add it, if not, do something else.
Below is part of a PHP database class someone else wrote, I have removed about 80% of it's code, all the un-related code to my question has been removed and just the amount remains that allows me to test this class without actually hitting a real database.
This class has a couple methods that let you set a key and value it then turns it into a mysql UPDATE and INSERT sql query using an array. I am trying to figure out how to use this code 100% so I can use this feature of it for UPDATE and INSERTS in my own application.
Basicly from what I gather you do something like this...
// assign some key/values to insert into DB
$db->assign('name', 'dfgd');
$db->assign('age', 87);
$db->assign('sex', 'female');
$db->assign('user_id', 4556);
// Do the insert
$db->insert('testing2');
Now where I am confused is I can keep on running code like this over and over on the page and it always will use the correct set of key/value array sets. Above you can see I used the assign() method 4 times and then call the insert() method which creates this
INSERT INTO test (name, age, sex, user_id) VALUES (jason davis, 26, male, 5345)
Now if I run another set like this on the same page...
// assign some key/values to insert into DB
$db->assign('name', 'dfgd');
$db->assign('age', 87);
$db->assign('sex', 'female');
$db->assign('user_id', 4556);
// Do the insert
$db->insert('testing2');
It then creates this...
INSERT INTO testing2 (name, age, sex, user_id) VALUES (dfgd, 87, female, 4556)
So how does it not combine the 2 sets of 4, so instead of inserting 8 record on the second insert, it completey replaces the first set of 4 values with the new set. This is great and what I want but I do not understand how it is happening? Also can this be improved anyway?
Below is a full class and my demo code, it can be ran without needing to connect to mysql for this demo, it will print to screen the SQL that it builds.
Also where would the public function reset() in the code below need to be used at, or would it not be needed?
<?php
class DB{
public $fields;
public function assign($field, $value){
$this->fields[$field] = ($value)==""?("'".$value."'"):$value;
}
public function assign_str($field, $value){
$this->fields[$field] = "'".addslashes($value)."'";
}
public function reset(){
$this->fields = array();
}
public function insert($table){
$f = "";
$v = "";
reset($this->fields);
foreach($this->fields as $field=>$value){
$f.= ($f!=""?", ":"").$field;
$v.= ($v!=""?", ":"").$value;
}
$sql = "INSERT INTO ".$table." (".$f.") VALUES (".$v.")";
//print SQL to screen for testing
echo $sql;
//$this->query($sql);
return $this->insert_id();
}
public function update($table, $where){
$f = "";
reset($this->fields);
foreach($this->fields as $field=>$value){
$f.= ($f!=""?", ":"").$field." = ".$value;
}
$sql = "UPDATE ".$table." SET ".$f." ".$where;
echo $sql;
//$this->query($sql);
}
public function query($_query){
$this->query = $_query;
$this->result = #mysql_query($_query, $this->link_id) or die( $_query."<p>".mysql_error($this->link_id) );
return $this->result;
}
public function insert_id(){
return #mysql_insert_id($this->link_id);
}
}
// start new DB object
$db = new DB;
// assign some key/values to insert into DB
$db->assign('name', 'jason davis');
$db->assign('age', 26);
$db->assign('sex', 'male');
$db->assign('user_id', 5345);
// Do the insert
$db->insert('test');
echo '<hr />';
// assign some key/values to insert into DB
$db->assign('name', 'dfgd');
$db->assign('age', 87);
$db->assign('sex', 'female');
$db->assign('user_id', 4556);
// Do the insert
$db->insert('testing2');
echo '<hr />';
// assign some key/values to UPDATE the DB
$db->assign('name', 'jason davis');
$db->assign('age', 26);
$db->assign('sex', 'male');
$db->assign('user_id', 5345);
// DO the DB UPDATE
$db->update('blogs', 'WHERE user_id = 23');
?>
Key in associative arrays are unique; assigning a new value erases the old.
If you still open for another database abstaction library, I want to suggest you to use AdoDB. It's can connect to multiple database, so you code will stay the same if you decide to switch database later. It have build in feature to sanitize data before insert/update.
For your code above, when you use AdoDB, you will write it like this:
$adodb =& ADONewConnection($dsn);
$data['name'] = 'dfgd';
$data['age'] = 87;
$data['sex'] = 'female';
$data['user_id'] = 4556;
// Do the insert
$result = $adodb->AutoExecute($table_name, $data, 'INSERT');
//If update, must have one of the key, such as id column
$result = $adodb->AutoExecute($table_name, $data, 'UPDATE', "id=$id");
You can read the documentation from the site, or inside zip file that you can download. I always use this library in all my project, even I prefer it more that build in CodeIgniter database library.
insert() and update() should (originally) set the $this->fields property back to an empty array upon execution, but you somehow (wrongly) deleted that code?
Update your code to this:
public function insert($table){
$f = "";
$v = "";
foreach($this->fields as $field=>$value){
$f.= ($f!=""?", ":"").$field;
$v.= ($v!=""?", ":"").$value;
}
$sql = "INSERT INTO ".$table." (".$f.") VALUES (".$v.")";
$this->reset();
//print SQL to screen for testing
echo $sql;
//$this->query($sql);
return $this->insert_id();
}
public function update($table, $where){
$f = "";
foreach($this->fields as $field=>$value){
$f.= ($f!=""?", ":"").$field." = ".$value;
}
$sql = "UPDATE ".$table." SET ".$f." ".$where;
$this->reset();
echo $sql;
//$this->query($sql);
}
Ok, we have come to the conclusion that my previous answer was right:
Because you use the name keys, it replaces the old keys with the new keys.
$db->assign('user_id', "1");
basically does this:
$this->fields['user_id] = (1)==""?("'1'"):1;
And when you got to do it again, it replaces it
$this->fields['user_id'] = (2)==""?("'2'"):2;
Try doing an assign, and then only assign the user_id again, there rest of the data will stay the same.
To fix this problem, we would call the $this->reset() function after a query.
public function query($_query){
$this->query = $_query;
$this->result = #mysql_query($_query, $this->link_id) or die( $_query."<p>".mysql_error($this->link_id) );
$this->reset();
return $this->result;
}
or you could call it in the individual insert or update functions:
public function insert($table){
// .... stuff
$this->query($sql);
$this->reset();
return $this->insert_id();
}
The other possibility is that the original programmer didn't convey his intent to you well enough. He might expect you to call $db->reset() after every query.