Im working on an in-house developed app, written in PHP.
We have several employee users that can make dozens of different changes, such as: add new employee accounts, courses, students, questions, tests etc. as well as modify or delete those.
My goal is to write a function(s) to log in a MySQL table any change that someone makes.
I can get this done one by one for each request.
So for example, a request can be sent by an user to add a new employee account to the database.
if(isset($_REQUEST['newemployee'])){
$db = new db();
$insertrow = $db ->insertRow("INSERT INTO employees (firstname, lastname, email, role, color) VALUES (?, ?, ?, ?, ?)", array($_REQUEST['firstname'],$_REQUEST['lastname'],$_REQUEST['email'],$_REQUEST['role'],$_REQUEST['colorpicker1']));
}
what I can do is write an if statement for any individual request.
E.g.
// I already have a table named logs, its very dirty here but you get the idea, it does the job.
if(isset($_REQUEST['newemployee'])){
$done_by = $_SESSION['loguser']['id']; // this is how we track the user ids.
$action = 'created new employee account, email: '.$_REQUEST['email'];
$db = new db();
$insertrow = $db ->insertRow("INSERT INTO logs(user_id, action) VALUES (?, ?)", array($done_by, $action));
I would like to, if possible, generalize/automate this process as much as possible to avoid writing dozens and dozens of if statements for each REQUEST that can be triggered.
My idea was something like this:
if (ANY REQUEST is triggered){
# tell me what it is (e.g. 'newemployee')
# here I will insert the log in to the DB. The `action` field will not be very specific in this case, but I can live with that.
}
I can't seem to just generally if REQUEST though, is it even possible?
Related
I am trying to restrict users records display. Basically i am developing a CRM where a user logs in and enters (orders, quotes...) his data and submits to database. So when a user logs in i want to display only those records which he has entered not others data. For this I take user's session id and stores it infront of his records,
While displaying i am doing like this
"SELECT * FROM orders WHERE user_id='".$_SESIION['userID']."';
But i have to do this 'WHERE' for all display function . Instead of that is there any other way i can do this i mean without repetitive WHEREstatement?
Please suggest
Your question: is there any other way i can do this i mean without repetitive WHEREstatement?
My answer is: There is no!
Help for prepare: https://www.w3schools.com/php/php_mysql_prepared_statements.asp
The database could be like this, for example:
table users
id_user email password etc.
Table articles
id_article title_article body_article id_user etc
When you query
select * from article where id_users = ?
Use prepere
insert into articles (............, id_user, ....) values (?, ?, ? etc)
All tables, like this one article, can have field id_user, it goes in table with query input ...., by session and prepere protection against SQL injections. And when user select anything, he will have only what he has injected.
I have two tables:
people:
peopleID (PK)
name
peopleaddress:
addressID (PK)
peopleID (fK)
address
addresstype
...other fields...
A person can have more addresses.
I have a form to add a new person (and addresses) and another form to edit info. When I load the edit form, it takes the info from the DB about that person and put them in the fields value="".
Right now, when I submit the edit form, I use 2 sql commands:
$stmt = $conn->prepare("DELETE from peopleaddress WHERE peopleID=?");
and
$stmt = $conn->prepare("INSERT INTO peopleaddress (peopleID, addresstype, active, street.....) VALUES (?, ?, ?, ....)");
It works well, only downside is that addressID changes every update and it tends to grow fast.
Am I doing it the right way or there is a way in php or better sql to say:
if new address exists » update
if doensn't exist » insert
if all fields of existing address empty » delete
Thanks for your help!
Here's what you're looking for: "INSERT ... ON DUPLICATE KEY UPDATE"
INSERT INTO peopleaddress (peopleID, addresstype, active, ...) VALUES (1,2,3, ...)
ON DUPLICATE KEY UPDATE addresstype=2, active=3, ...;
It will find the address if it is already in the database and update it, if it can. Otherwise, it does the insert.
You may have to re-work your '?' substitutions.
If someone adds a new address to an existing account it's okay to have a new row (e.g. id), keeping old addresses isn't necessarily a good or bad thing and having that as a separate table is good (some people may have a home and a business address in example).
If all the fields are missing then your code's logic (PHP?) shouldn't even be getting to the point of any SQL queries.
function admin_members_address_update()
{
// 1. Create $required array.
// 2. Check foreach $_POST[array_here].
// 3. If any of the required $_POST variables aren't set HTTP 403 and end the function.
//if something wrong, HTTP 403
else
{
///Normal code here.
}
}
You should always check for variables you expect to be set just like you always escape (e.g. not trust) client data and have your error reporting set to maximum. A hacker will omit a form field easily with any dev tool and try to find vulnerabilities based on error messages generated by the server.
Always check for failure before you presume success.
//if () {}
//else if () {}
//else if () {}
//else {}
SQL should be handled in a similar fashion:
$query1 = 'SELECT * from table;';
$result1 = mysqli_query($db,$query1);
if ($result1) {}
else {/*SQL error reporting, send __magic_function__ as param with well named functions*/}
That is the very rough of it (don't have access to my actual code) though it should give you something to go on. Comment if you'd like me to update my answer when I have access to my code later.
A MySQL unsigned INT (max 4294967295) will support an average of 13 inserts per second, 24x7, for 10 years. That's already probably as many addresses as there are on earth. If you're pushing the limit somehow, a BIGINT will be massively larger and probably never run out in a million years.
Basically, don't worry about using up IDs with auto increment, the decision of whether to DELETE -> INSERT or UPDATE/DELETE/INSERT should be based on whether you need to maintain persistent IDs for individual addresses. Deleting then inserting assigns a new ID, even if it's really the same address, which is undesirable if you want to create a foreign key that references address IDs. If you don't need that you don't need to worry about it, though as a personal preference I would probably incorporate UPDATE.
Here is the relational schema of my DB
Where 'articol' is 'paper', 'citare' is 'citation' in English:
A user is logging in with an ID and a Password and using the interface, he introduced his data in the DB. The code works very well for adding a 'paper' but now I want the user to introduce his 'citations' of the respective 'paper'. The problem is that the code allows introducing the citation in the table 'citation'('citare') but I do not know how to associate this 'citation' to 'paper' ('articol') and to the logged 'user' in the PHP code?
session_start();
$user = $_SESSION['username'];
$pass = mysql_real_escape_string(encrypt_decrypt('encrypt', $_SESSION['password']));
//there is code here...
$AddQuery="INSERT INTO `citare` (articolCitat, titluCitare, autoriCitare, publicatieCitare, isiCitare, volCitare, noCitare, anCitare, linkCitare) VALUES ('$_POST[narticolcitat]','$_POST[ntitlucitare]','$_POST[nautoricitare]','$_POST[npublicatiecitare]','$_POST[nisicitare]','$_POST[nvolcitare]','$_POST[nnocitare]','$_POST[nancitare]','$_POST[nlinkcitare]' )";
mysql_query($AddQuery, $link);
$last_id_inserted_from_citare = mysql_insert_id($link);
$AddQuery="INSERT INTO artcitare (idArticol, idCitare) VALUES ('{$_POST['hidden_articol']}','".$last_id_inserted_from_citare."')";
mysql_query($AddQuery, $link);
In terms of what you want to know, basically you cannot insert into 2 different tables at the same time (hence INNER JOIN does not help in this case). You need to do one first and then the other.
You can wrap those in a transaction, which will make it more robust.
The order is important. You first need to insert you main entities and then fill in the association tables for your many to many. That means that in order to associate "citare", "articol" and "user" they'll need to be inserted first. After that, you'll need to create the appropriate INSERTs in the "artcitare" and "userart" tables to associate them.
Having said that, I think that you might be confusing the process of persisting data into the database from its retrieval process. From your code sample, it seems that the entity "articol" is already persisted in the database, and you're only trying to associate "citares" to it. If the "articol" entity is already persisted, I'd assume that the corresponding "userart" association also exists. Hence, when you're inserting "citares" and associating them to "articols" via the "artcitare" association table you're already linking them to the user.
Currently I have an application whereby when the user click the submit button, it will trigger the insert code to insert the record for reports function.
The problem I'm facing now is, the user not a computer savvy, therefore I've an inaccurate data from the table. Below are the few examples that cause the data capture incorrectly.
Case 1: Once the user click the submit button, the data already insert to the database but the user accidentally hits the submit button again and the insert trigger again and causing the database has two same records with a different timestamp in about 5 to 10sec.
Case 2: The first record user enter is actually not the user wants, for example, the user wants to key-in 29/10/2013 but the user key-in as 28/10/2013 and the user again hit the back button and insert the correct one which is 29/10/2013 and a single transaction it have 2 record which is one is the correct date and one is the mistake date.
Below are the code:
$stmt = $db->prepare("INSERT INTO log (dob, log_datetime, log_count, amount) VALUES (?, ?, ?, ?)");
$stmt->bindParam(1, $userDate);
$stmt->bindParam(2, $now, PDO::PARAM_STR);
$stmt->bindParam(3, $cust_count);
$stmt->bindParam(4, $amount);
date_default_timezone_set('Asia/Kuala_Lumpur');
$now = date('Y-m-d H:i:s');
$cust_count = 1;
$amount = 10;
$stmt->execute();
The code above trigger whenever the user hits the submit button, so I was thinking is there possible to make the insert code to trigger after a period of time, example 30sec?
Additional Notes
This is an application developed and execute the database from the table
It has nothing to do with form validation, as I already coded the form validation part.
My question is is there any way to trigger the insert query on specific amount of time set
I search in SO and Google, but I couldn't find a relevant example to work with.
Please suggest me any method that can fix the above two case scenarios.
Thank you.
For the first case, disable the button when the form is submited.
For the second case, validate the date before inserting it into the database.
http://us3.php.net/sleep
sleep(30);
$stmt->execute();
Will do what you want I believe.
MySQL doesn't have that behavior. I think that what you are looking for is an asynchronous task queue, like Celery
Disable the submit button upon submitting, and on the post-submission page, show the user the submitted data and give them a clear way to edit it.
Users generally suspect that hitting back into a form is not a good idea, but you're not giving them any better options.
Trying to catch all UI leaks can prove difficult and cumbersome.
I would rather use the timestamp in the DB as a temporal filter
I would query the most recent records (in the last 30 seconds) using cust_count as a candidate key, and update the last record (if any) instead of creating it anew.
Schematic example:
function update_log ($entry)
{
$recent = $db->select (
"id FROM log
WHERE timestamp > now()-30 seconds
AND user = $entry->user");
if ($recent)
{
// overwrite last record with current entry (and refresh timestamp)
}
else
{
// create a new entry
}
}
You could even delete all logs of the user adding an entry that are newer than 30 seconds.
I'm currently working on my private messaging system for my website and so far some of what I have to do is making sense.
I've figured I have to use the power of mysql join but have a few questions..
In my model I have started coding for my message inbox and have the following query:
"SELECT * FROM messages WHERE from_id = '$id'";
$id is basically the auto-incremented id from my users table and from_id is the same thing but in my messages table.
What I want to know is how is the message table suppose to get the from_id? I know that the from_id from the messages table and the auto-incremented id from the users table are what I will use to link both tables.. but I'm slightly stuck how to go about this.
I would rather use id's to identify the sender and receiver of a message than their username but I'm quite confused how to go about setting this up. I'm quite certain I can do it once I have an understanding of it.
depending on what auth library you are using, you will need something like this in CI:
view_messages()
{
//this check could be done in the constructor
if($this->auth->logged_in()==TRUE)
{
$userID=$this->auth->user_data('userID');
}
else
{
redirect('not_logged_in');
}
$this->load->model('message');
$messages = $this->message->get_messages($userID);
// do what you want with messages.
//print_r($messages);
}
your messages model:
function get_messages($userID=null)
{
$this->db->where('userID', $userID);
$data = $this->db->get('tblMessages');
return $data->result();
}
I'm not quite sure if I fully understand you.
You are loading the messages, within the messages table you have a column for the user id but you want to load the users name aswell with a single query?
If so than something like this would do it
SELECT {whatever you want to have} FROM users
u, messages m WHERE u.id=m.from_id
than in your SELECT claus you can retrieve the users details with u.username assuming you have a username column in your users table.
Can't you use session to save the user id when the user login?
That way you can have the user id when needed.
// ...
$fromID = $_SESSION['uid'];
INSERT INTO `message` (`from_id`, `to_id`, `time`, `message`) VALUES('$fromID', '$toID', '$time', '$message')
Of course, we haven't talk about security here, so the code's obviously not secure. But we'll leave that for another discussion. Have I kicked you in the right direction? :p
There is more to it than just knowning a little about MySQL joins. There are lots of systems you can download to get a good idea, try phpBB or some other forum software.
With Private Message systems its very easy to make something rubbish, but good ones are tough.
Remember, if your message only has one entry like the example proivided by another user:
INSERT INTO `message` (`from_id`, `to_id`, `time`, `message`) VALUES('$fromID', '$toID', '$time', '$message')
That will need to show in the Inbox of the to user, and the sent box for the from user. If the to_user deletes it, the from_user suddenly has no record of the message.
You need to create a join table to list all the attachments of users (to or from) joined to the message id. Then you can have multiple to users as well!
Messages should always exist, users just "trash" (set is_trach to 1 on their connection). When users delete from trash they delete only their connection to the message, and when the last user has deleted their connection THEN you can delete it (or get a cron job to delete orphaned messages).