In my website, I allow groups of users to manage a group bank account. Users report that they payed for something the group should fund, and the group account 'pays them back'.
However, I want to give the account admin control over which user pays what and when.
For example, a group of people share a car. They open a group account to handle their car-related expenses - gas, maintenance etc.
The group admin decides that only people who drove over 20 miles this month should participate in the gas bill, and the bill is divided between the paying users (those who drove over 20 miles) proportional to their mileage (a user who drove 60 miles will pay twice as much as a user who drove 30 miles).
I have a table for users (a list of users, each one is part of a group account), one for group_accounts and one for expense_types (in this example, gas and maintenance are listed here)
The question:
Is there a good way to store client-defined queries or algorithms in a database to allow the admin of the group to create complex billing methods, and (using PHP) subsequently retrieving and implementing those algorithms and calculating the result?
This is a database design question, so as far as I'm concerned extra columns may be added to any table, new tables created, procedures, triggers etc.
NOTE: THE SPECIFIC EXAMPLE IS OF NO REAL INTEREST, I want my system to handle algorithms of unknown complexity that use data from unknown tables and columns.
Related
For now, the system should have 6 different user levels.
Each level will be gained upon user activity, for example:
Level 1 - When user register
Level 2 - When user completes a mission
Level 3 - When user completes more than one mission
Level 4 - When user donate > $X amount of money
Level 5 - When user write more than 50 comments in blog
Level 6 - When user complete quiz
And now... I'm wondering, what's the best database schema to achieve this? I should keep track of all actions related to user's activities, that's why I though about xp_events table... Something like this:
id # primary key
event # type of event, e.g. 'register', 'complete_quiz', etc.
user_id # id of user
delta # number of "exp" which will be gained after specific action
And... in users table I will keep record of current level and "exp" which each user has earned until now.
When user makes any activity, I will call a trigger which will check if user have new level unlocked.
But... I'm aware that in long term (e.g. if more levels are added), this isn't optimal solution.
Looking forward for any suggestions.
I see two obvious possibilities here.
One is to have an event table like you say, with a user ID, event ID, dollar amount (for donations), probably a date/time, maybe other data. If the only reason why you are keeping any of this data is to determine each user's level, this is simple and effective.
If you're keeping track of this data for other purposes also, you probably want to separate it into multiple tables. In such a case you would likely have other data you need to keep for each event. Like for a donation you would need dollar amounts, which I assume don't apply to comments and missions. For comments you likely need the text of the comment and some indication of the thread this comment is on or what it's subject is. For a mission -- I don't know what a "mission" is in this context, but you likely want some information about the type of mission and where it was or who they were supposed to kill or whatever. Most of this data would not be applicable to events of different types. A comment probably doesn't have a dollar amount, a mission doesn't have a thread, etc. So you'd end up with a lot of irrelevant data and bunches of null fields.
In my simple system there is a users table where user logins and passwords are stored as well as a customers table.
Users can be related to customers in 3 different ways.
1) Sales Representative to the customer
2) Lead generated by this customer
3) Customer account entered by this rep
Originally I planned on having all on the customers table:
customers.user_id customers.lead_id customers.entered_by_id.
With CakePHP is this the wrong way? How should it be designed?
I am 1 day new to CakePHP.
Without knowing more about what you are building and your requirements, this is what I would do:
users: contains just authentication info
users.role: useful for querying what role a specific user is (customer, sales rep, admin, etc.)
users.username
users.password
sales_representatives: contains sales rep data
sales_representatives.user_id: Links the sales rep data to a specific user
customers: contains customer data
customers.user_id: Links the user to a specific user (assuming you want them to log in, if not you can skip this)
customers.sales_representative_id: Links a customer to a sales rep. You might want to store a history of sales reps for a specific customer in a separate table, but this field is just the current sales rep.
customers.lead_id: Links to a specific lead this customer came from. Can be null in case it was inbound and not a lead, but will probably link to something useful.
leads: Contains lead data
leads.sales_representative_id: Contains the current sales rep for a given lead. As with the customers table, you might want to store a list of historical sales reps for a given lead in a separate table.
You might also optionally add a user_id to the lead table if a lead can login, but that might not be the case in your system.
What is your entered_by_id? That seems more like a lead-related id, in which case you may want to track that in the leads table (separate from the sales_representative_id).
Most of this stuff isn't CakePHP-related, though good schema planning will go a long way to making using CakePHP easy :)
I'm currently working on a custom add-on for a forum, where it checks a json file to see if a user has permission to moderate a certain group. I was wondering in what order I should store this information.
Let's say my demo information is: User 1 and 2 have access to edit Group A. User 1 also has access to edit Group B.
Order 1 - ID First, Then Group (Mockup)
{
"1": [
"A",
"B"
],
"2": [
"A"
]
}
Order 2 - Group First, Then Id (Mockup)
{
"A": [
"1",
"2"
],
"B": [
"1"
]
}
Which one is easier to manage/work with when you're using php? (Or are either of these wrong when storing data in json? Should I be using some other combo? I don't want to use MySQL for this, since it's going to be a really small set of users who can manage these groups)
Whenever I see a question like this, where there is not really a correct answer, as there are so many possibilities, I tend to think the OP is just trying to be as exhaustive and pedantic as possible in their research to find the best option, and that is really a great kind of curiosity, suited perfectly for programming. Knowing this, here are some thoughts.
Thoughts
I think you should look at this in a different way. When users can be differentiated by the privileges they are granted, you are essentially defining roles. Every role is distinguished by its level of access to content and how much that content can be modified.
When framing it like this, think of a pyramid. The most privileged roles have the best overview of all content, and consequently will be able to modify it according to their discretion. There are very few of these people. Descending the pyramid results in more and more people for every level of the pyramid, with an ever decreasing overview of the content and the ability to modify it. This continues until you reach the bottom of the pyramid, or the "Basic" user role, which the majority of the members of any community will be granted by default. They are the foundation of the pyramid but have very few privileges.
This means that users at the top of the community are granted more weight in their decisions. For example, say the highest role in a community is "Administrator." Any user granted this role can do anything. That is a lot of responsibility to carry. To put this in weighted terms, this role will be assigned a weight of 100. Beneath the Administrator are the "Moderators." They are essential to the community, but granting them the ability to delete entire categories and all child content might be too much power; they should be allowed to ban users and delete some content. If an Administrator fires them before demoting their role, they can totally destroy the community. Knowing this, Moderators do carry weight, but not as much as Administrators. They will be assigned a weight of 75. The community needs "Janitors" to clean up spam and reposts, but they should not be allowed to ban or delete users. Consequently, you assign them a weight of 50. This distribution of weight continues until all roles have been defined, each with distinct privileges.
The Question
Tying this together, sure, you can hardcode a specific user ID to a specific group ID, or vice versa, but that can become difficult to manage at some point, especially considering the duplication involved.
Consider then, that groups should only be accessed by users with a given weight. For example, Group A is assigned a user access weight of 25, and group B is assigned a user access weight of 10. What this means is that any users who have a weight of 25 or more will have automatic access to group A, and of course because group B only requires a user with a weight of 10, any user that can access group A can definitely access group B as well, because 25>10. In pyramid terms, 25 is equal to or greater than groups assigned a user access weight of 25 or 10. With this type of setup, there can be dozens of groups, each with their own user weight access. Further, that is the only variable that needs to be assigned to a group, instead of having to keep complicated user access lists for every group, or vice versa, with users keeping track of ever-growing lists of group IDs they can access.
With this knowledge, consider the example you provided. User 1 has access to edit group A and B. User 2 only has access to edit group B. This implies that User 1 has more privileges. If user 1 has more privileges, he also happens to carry more weight than user 2. Let us say that user 1 is assigned a weight of 30, while user 2 is assigned a weight of 15. Tying into the group scenario just described, this would mean that user 1 (weight: 30) can definitely access both groups A (weight user access: 25) and B (weight user access: 10), because 30 > 25 > 10. User 2, on the other hand, can only access group B, because 15 > 10. Furthermore, this means that while user 2 cannot access group A, it does not mean that he could not, for example, access group C, with a minimum user weight requirement of 5, because 15 > 10 > 5. In addition, this obviously means that user 1 could also access group C.
Afterword
You wake up one day and realize that group B should not be accessed by the community proletariat, with their peasant role weights of 10. Group B should be for the more dignified of the community, with weights of, like, 11 or more, so you increase the minimum role weight requirement of group B to 11. With a single change to a single integer assigned to group B, you have just prevented every single user with a weight of 10 or less from accessing their once cherished group B. The prole is not happy, but you do not care, because you are the community god, with a weight well over 9000, and you can do whatever the hell you want. You can also do it without having to modify dozens, hundreds, thousands, or millions of specific user/group access lists that are, as mentioned, way too clunky and unmanageable.
This is how smart access control is managed.
I'm actually worried about designing the perfect tables to handle transactions and commissions. especially that we really don't want tables to be out of sync in any case.
Basically this is like ebay, where merchants sell to buyers and we get a commission out of each transaction.
The app is already built using Codeigniter, and it has:
Merchants(id,name,balance)
Transactions(id,merchant_id,buyer_id,amount,transaction_id)
Now what other tables to make if needed to make us able to fetch our revenue and the seller's revenue, and what's the best DB mechanism to process a refund?
And should we make another table of each deposit to the merchant's balance rather than +x the balance row.
Whenever you are dealing with any financial tasks, always store as much information about it as possible.
There are two ways of keeping all your tables in sync.
Database level - using options such as stored procedures and triggers
Application level - Use transactions (innoDB) and make sure that the transactions are atomic. (I would suggest to go with this)
Regarding the database design -
Add a column commission to Transactions that tracks your commission. Data for this can be either calculate beforehand when pricing the item or on the fly during the transaction.
The buyer pays product cost + commission, seller gets product cost and you get commission. If your commission is a variable (not a flat rate and changes from item to item), you could have a column in the Items table which stores it. Things should be trivial from now on. You just have to maintain a History table that keeps a list of transactions with respect to the user. It would look something like
AccountHistory(id, transaction_type, transaction_id, amount)
You could split AccountHistory into two - one for incoming money and one for outgoing money.
So everytime a transaction takes place the following happens
Entry into to Transactions table.
Entry into AccountHistory for buyer
Update buyer balance
Entry into AccountHistory for seller
Update seller balance
All this should be atomic.
For rollbacks, you could add a column transaction_type to distinguish rollbacks from normal transaction or you could maintain a separate table.
Having worked with a database where the design had financial transactions scattered around a bunch of different tables, I can tell you that it is much better to have all transactions in a single table. You should add transaction type to your transaction table (and a look up table for the transaction types).
Then each transaction can have a separate line for commission and seller product cost. Transaction that are undone later would get entered in as negative amounts. It is also helpful for these types of things to add a parent transaction field so that you can easily identify which transaction it was changing.
All data entry that forms part of a transaction should be in a stored proc and have transactions so that if one part of the insert fails, the entire thing is immediately rolled back. This is critical. It needs to be in a stored proc so that no one (except systems dbas) has direct access to tables that store financial records.
When you start to think of handling financial transactions the very worst thing you can do is design for the user interface alone. You need to design for the financial reporting you will need to have. You need to consult the people who will be auditing your finances to see what information they need and what internal controls they expect you to have in place to prevent internal as well as external fraud.
I am developing a web portal which will store the job requirement like, experience, salary etc in a database and whenever any user (new/old) matches that criteria the job should display him in his dashboard after he logins.
My Columns in employees are
Age, City, Industry, Marital status.
So, when admin post the jobs, he will define the criteria which user can see this. For ex. Age between 20-30, City only Mumbai like that.
How do I store these information in database efficiently.
I am using PHP/MySQL.
You would ideally create a table with the user's:
Unique ID
Name
Marital Status
City
Age
Create a second table to pair industry and UUID, like so:
Unique ID
Industry
This is so that a given user can belong to more than a single industry.
Third, create a table to pair user IDs and experience:
Unique ID
Position
Industry
Start date
End date
Since industry and experience are data which a given user can possess an arbitrary quantity of, you need to abstract the data into its own tables. Don't try representing all of this information in a single table - it's a solution that scales poorly past a single employer.
I'd also like to note that if your application is going to be deployed in the United States and several other nations, it's actually illegal for employers to discriminate based on age and marital status. I'm assuming this doesn't apply to you, but there it is.
in terms of speeding up your look ups, the most important thing you'll want to do is make sure you index the columns that you will be searching against.
So for instance, if you want to do a search that is based on someonen's start date:
like:
select * from tablename where start_date > 'some date';
then it's very important that you index the start_date column on the 'tablename' table.
Apart from making sure that your tables are orthongal deciding what the best way of setting up your database you'll want to ask your self what kind of questions will you be asking your database and design your tables around those questions.