Storing credits in database - php

Just a quickey. I am developming website, where you can buy credits and spend them later for things on the website.
My question is, is it ok to store amount of credits with user (user table, column credits and iteger amount) or it is necessary (or just better) to have separate table with user id and amount ?
Thanks

Both actually.
Considering that you'll be dealing with monetary transactions to get those credits, you want to be able to get a log of all transactions (depending of the laws in your country, you will NEED this). Therefore you'll need a credits_transactions table.
user_id, transaction_id, transaction_details, transaction_delta
Since programmatically calculating your current credit balance will be too costly for users with a lot of transactions, you'll also need a credit_balance row in your user table for quick access. Use triggers to automatically update that column whenever a row is inserted from credits_transactions (technically, update and delete shouldn't be allowed in that table). Here's is the code for the insert trigger.
CREATE TRIGGER ct_insert
AFTER INSERT ON credits_transactions
BEGIN
UPDATE users SET credit_balance = credit_balance + NEW.transaction_delta WHERE user_id = NEW.user_id;
END
;;

I also have sites containing credits and found it easiest to store them in the user table, mostly because you need access to it on every page (when the user is logged in). It is only an integer so will not do much harm. I think actually creating a new table for this value might be worse perfomance wise because it needs an index aswel.
A good rule of thumb is to create a user table for the info you need on every page, and normalise the data you dont need on every page (for example adress information, descriptions etc).
Edit:
Seeing the other reactions,
If you want to have transaction logs aswel I would store them seperately as they are mainly for logging (or if the user wants to view them). Calculating them on the fly from the log is fine for smaller sites but if you really have to squeeze performance just store the actual value in the user table.

If you store in separate table, you can keep log of changing the credits. If you store in column, you will have only the current amount of credits.

If you want to keep a record of Credits History Log like
how many credit bought today.
how many spend yesterday.
what did you bought with credits
I think its better to put this in a separate table. In this way you can get these kind of results by applying mathematical operations.

Credits are like money. If a user needs to purchase them, then they are money. Money is tracked using accounts. Account has associated transactions, deposits and withdrawals -- and balance. Search the SO or google for database and account. Here are just a few examples:
one
two
three

I'd have a table which stores the purchases and bought credits, with user id.
Then calculate each time based on this, it should be fast if it's indexed, this way you will be able to easily have a purchase history.

Related

Mysql - Prevent duplicate entry for incremental value in a field

My site hosted in a shared hosting. It's a POS application (PHP, Codeigniter). It has several users. Everyone is generating invoice. Invoice number is incremental. That is when a user submit a invoice form, it fetches the last invoice number then increment it by one and then create a new row with new invoice number. This process some time (very rarely) duplicate invoice number generated when users submits the form pretty much same time.
One possible way is that make invoice unique. But if it happens again, user will see an exception or formatted error message.
I don't want show error to my users. Because when they submit the invoice form , it contains sales information that they have written. If they loose it because of this warning, they feel disturbed. AJAX will not work. Direct submit is working here(for invoice submission ).
Can SQL lock be applied for this situation? I have no idea about SQL locking.
If your concern is not performance an inefficient way to do would be something like
Insert your invoice number as null/zero and have another query update that
like
INSERT INTO invoices (id, invoice_number) VALUES (10001, null);
UPDATE invoices SET invoice_number = id WHERE invoice_number IS NULL;
For locks you can look into SELECT ... FOR UPDATE that would lock the last read row, and also inserts from other connections are also blocked but its better you try it on your DB as this depends on your Mysql version and isolation levels set.

Merging 2 table fields and inserting into another field in the same record

I am not sure if what I am trying to do is even possible but, if it is, I am obviously not Googling properly and would appreciate any assistance I can get here, even if it is just a link to an "Idiot Guide".
Okay, at the moment, I have a database table of 150-odd records. Each record contains basic details (name, location, contact information, etc.) and login credentials (UserID, password, et al). These details are captured by the website admins (i.e. no general public registration) after the prospective user has undergone a successful interview process. When a record is created, a 6-char "username prefix" is assigned to the user (e.g. 'UNPREF') and this, along with the auto-incremental UserID (e.g. 125), is used as the username (e.g. UNPREF125) to log into the website. However, the username is not actually stored in the the database. Instead, when a user logs in, the login script splits the provided username and the two chunks are checked against their relevant fields.
In addition to this primary user table, there are a number of other tables which contain additional information (for instance, educational qualifications, work history, etc), which are linked to the user by means of the UserID, as per the primary table. Now, both users and admins can update a user's data and, therefore, I have created a field for each row that logs who last modified the record (modby) and when (modon) so that, if there are any shenanigans, I can ascertain who fiddled last and, in theory, deal with that particular individual without any "he said/she said" nonsense.
Now here is the tricky bit. My users and admins are stored in separate databases on separate servers (the latter being beyond my control) but I have recently discovered the joys of Federated Tables, which work brilliantly. One small quirk, tho; because my users and admins are stored in separate databases and because I want to maximise the number of records I can store in a single database (there is a size limit of 100mb per database), with the company's current rate of expansion and each branch requiring two admin accounts, it is not an improbable scenario that a user and an admin will end up with the same UserID. Therefore, the modby fields store the full username (i.e. UNPREF125 - admins get their own, unique Username Prefix so as to differentiate between admins and users)
Now, perhaps it is because I am such a newbie at Federated Tables but I can't seem to find a way to compare a field in a table on Server A (i.e. modby) with 2 separate fields (i.e. unprefix and userid) in the Federated Table, called from Server B, but I have come up with a workaround by creating an additional field in Server B's table, namely username, which stores the merged values (namely 'DBPREF125') and modby is checked against this instead, which works fine (I'm sure there is an easier way but I will save that lesson for another time).
Now, here is my question. The admin table is currently small (only 26 records) and so I captured the usernames manually, using phpMyAdmin, but I would prefer to avoid having to manually create usernames for the 150+ records in my users table. Is there any way I can get MySQL to pull the values of the userid and unprefix fields, join them together and store the result into the username field of the same record or would I need to turn to PHP for this and, if so, how would I go about this?
I apologise for the length of my question but I hope this will help explain why Google was not my friend today.
Many thanks in advance.
To store the combination in the table:
UPDATE TableB
SET username = CONCAT(unprefix, userid);
Or you can just use it when comparing:
SELECT *
FROM TableA a
JOIN TableB b
ON a.modby = CONCAT(b.unprefix, b.userid);

Saving stats data in MySQL Database for a line graph

I'm trying to make a pretty line graph to represent the rate of users registering to my site. What would be the best way to fetch and store my sites stat data to a MySQL Database?
I was thinking to use a cron job that would fetch the total amount of users from a seperate table, and subtract the previous total to get the amount of newly registered users. This value would be used for the line graph. To me, it seems like too much, especially if I want to get rate of users on a per-minute basis. Is there a more efficient way of doing this? Should I store each day in a separate row?
I'd suggest you include the time that users registered in your users table. You can then perform whatever analysis you like at a later date.
You can also create a field in table that is upgraded by a cron job (by minute, hour, day and so on), the field stores the status of user, new, old or other status that you have.
As said eggyal, you must also have a field with register date of user

What is an elegant / efficient way of storing the status of 100 lessons for multiple users?

I'm working on an app in JavaScipt, jQuery, PHP & MySQL that consists of ~100 lessons. I am trying to think of an efficient way to store the status of each user's progress through the lessons, without having to query the MySQL database too much.
Right now, I am thinking the easiest implementation is to create a table for each user, and then store each lesson's status in that table. The only problem with that is if I add new lessons, I would have to update every user's table.
The second implementation I considered would be to store each lesson as a table, and record the user ID for each user that completed that lesson there - but then generating a status report (what lessons a user completed, how well they did, etc.) would mean pulling data from 100 tables.
Is there an obvious solution I am missing? How would you store your users progress through 100 lessons, so it's quick and simple to generate a status report showing their process.
Cheers!
The table structure I would recommend would be to keep a single table with non-unique fields userid and lessonid, as well as the relevant progress fields. When you want the progress of user x on lesson y, you would do this:
SELECT * FROM lessonProgress WHERE userid=x AND lessonid=y LIMIT 1;
You don't need to worry about performance unless you see that it's actually an issue. Having a table for each user or a table for each lesson are bad solutions because there aren't meant to be a dynamic number of tables in a database.
If reporting is restricted to one user at a time - that is, when generating a report, it's for a specific user and not a large clump of users - why not consider javascript object notation stored in a file? If extensibility is key, it would make it a simple matter.
Obviously, if you're going to run reports against an arbitrarily large number of users at once, separate data files would become inefficient.
Discarding the efficiency argument, json would also give you a very human-readable and interchangeable format.
Lastly, if the security of the report output isn't a big sticking point, you'd also gain the ability to easily offload view rendering onto the client.
Use relations between 2 tables. One for users with user specific columns like ID, username, email, w/e else you want to store about them.
Then a status table that has a UID foreign key. ID UID Status etc.
It's good to keep datecreated and dateupdated on tables as well.
Then just join the tables ON status.UID = users.ID
A good option will be to create one table with an user_ID as primary key and a status (int) each row of the table will represent a user. Accessing to its progress would be fast a simple since you have an index of user IDs.
In this way, adding new leassons would not make you change de DB

Soft delete best practices (PHP/MySQL)

Problem
In a web application dealing with products and orders, I want to maintain information and relationships between former employees (users) and the orders they handled. I want to maintain information and relationships between obsolete products and orders which include these products.
However I want employees to be able to de-clutter the administration interfaces, such as removing former employees, obsolete products, obsolete product groups etc.
I'm thinking of implementing soft-deletion. So, how does one usually do this?
My immediate thoughts
My first thought is to stick a "flag_softdeleted TINYINT NOT NULL DEFAULT 0" column in every table of objects that should be soft deletable. Or maybe use a timestamp instead?
Then, I provide a "Show deleted" or "Undelete" button in each relevant GUI. Clicking this button you will include soft-deleted records in the result. Each deleted record has a "Restore" button. Does this make sense?
Your thoughts?
Also, I'd appreciate any links to relevant resources.
That's how I do it. I have a is_deleted field which defaults to 0. Then queries just check WHERE is_deleted = 0.
I try to stay away from any hard-deletes as much as possible. They are necessary sometimes, but I make that an admin-only feature. That way we can hard-delete, but users can't...
Edit: In fact, you could use this to have multiple "layers" of soft-deletion in your app. So each could be a code:
0 -> Not Deleted
1 -> Soft Deleted, shows up in lists of deleted items for management users
2 -> Soft Deleted, does not show up for any user except admin users
3 -> Only shows up for developers.
Having the other 2 levels will still allow managers and admins to clean up the deleted lists if they get too long. And since the front-end code just checks for is_deleted = 0, it's transparent to the frontend...
Using soft-deletes is a common thing to implement, and they are dead useful for lots of things, like:
Saving a user's data when they deleted something
Saving your own data when you delete something
Keep a track record of what really happened (a kind of audit)
etcetera
There is one thing I want to point out that almost everyone miss, and it always comes back to bite you in the rear piece. The users of your application does not have the same understanding of a delete as you have.
There are different degrees of deletions. The typical user deletes stuff when (s)he
Made a misstake and want to remove the bad data
Doesn't want to see something on the screen anymore
The problem is that if you don't record the intention of the delete, your application cannot distinguish between erronous data (that should never have been created) and historically correct data.
Have a look at the following data:
PRICES | item | price | deleted |
+------+-------+---------+
| A | 101 | 1 |
| B | 110 | 1 |
| C | 120 | 0 |
+------+-------+---------+
Some user doesn't want to show the price of item B, since they don't sell that item anymore. So he deletes it. Another user created a price for item A by misstake, so he deleted it and created the price for item C, as intended. Now, can you show me a list of the prices for all products? No, because either you have to display potentially erronous data (A), or you have to exclude all but current prices (C).
Of course the above can be dealt with in any number of ways. My point is that YOU need to be very clear with what YOU mean by a delete, and make sure that there is no way for the users to missunderstand it. One way would be to force the user to make a choice (hide/delete).
If I had existing code that hits that table, I would add the column and change the name of the table. Then I would create a view with the same name as the current table which selects only the active records. That way none of the existing code woudl break and you could have the soft delete column. If you want to see the deleted record, you select from the base table, otherwise you use the view.
I've always just used a deleted column as you mentioned. There's really not much more to it than that. Instead of deleting the record, just set the deleted field to true.
Some components I build allow the user to view all deleted records and restore them, others just display all records where deleted = 0
Your idea does make sense and is used frequently in production but, to implement it you will need to update quite a bit of code to account for the new field. Another option could be to archive (move) the "soft-deleted" records to a separate table or database. This is done frequently as well and makes the issue one of maintenance rather than (re)programming. (You could have a table trigger react to the delete to archive the deleted record.)
I would do the archiving to avoid a major update to production code. But if you want to use deleted-flag field, use it as a timestamp to give you additional useful info beyond a boolean. (Null = not deleted.) You might also want to add a DeletedBy field to track the user responsible for deleting the record. Using two fields gives you a lot of info tells you who deleted what and when. (The two extra field solution is also something that can be done in an archive table/database.)
The most common scenario I've come across is what you describe, a tinyint or even bit representing a status of IsActive or IsDeleted. Depending on whether this is considered "business" or "persistence" data it may be baked into the application/domain logic as transparently as possible, such as directly in stored procedures and not known to the application code. But it sounds like this is legitimate business information for your needs so would need to be known throughout the code. (So users can view deleted records, as you suggest.)
Another approach I've seen is to use a combination of two timestamps to show a "window" of activity for a given record. It's a little more code to maintain it, but the benefit is that something can be scheduled to soft-delete itself at a pre-determined time. Limited-time products can be set that way when they're created, for example. (To make a record active indefinitely one could use a max value (or just some absurdly distant future date) or just have the end date be null if you're ok with that.)
Then of course there's further consideration of things being deleted/undeleted from time to time and tracking some kind of audit for that. The flag approach knows only the current status, the timestamp approach knows only the most recent window. But anything as complex as an audit trail should definitely be stored separately than the records in question.
Instead I would use a bin table in which to move all the records deleted from the other tables. The main problem with the delete flag is that with linked tables you will definitely run into a double key error when trying to insert a new record.
The bin table could have a structure like this:
id, table_name, data, date_time, user
Where
id is the primary key with auto increment
table_name is the name of the table from which the record was deleted
data contains the record in JSON format with name and value of all fields
date_time is the date and time of the deletion
user is the identifier of the user (if the system provides for it) who performed the operation
this method will not only save you from checking the delete flag at each query (immagine the ones with many joins), but will allow you to have only the really necessary data in the tables, facilitating any searches and corrections using SQL client programs

Categories