PHP, MySQL - efficiently determining access permissions based on distant parent - php

I'm struggling to come up with an efficient solution to determine user access to a specified folder, using PHP (specifically Laravel) and MySQL. I want to create a system that has Google Drive-esque functionality...
For example, Joe Bloggs creates many folders within folders, e.g. Level 1 > Level 2 > Level 3 > Level 4 > Level 5. Within any of these folders, can be any number of additional sub files and folders.
This would be the resulting database structure -
Table name: users
| id | name |
| -- | ---------- |
| 1 | Joe Bloggs |
| 2 | John Snow |
Table name: folders
| id | parent_id | author_id | name |
| -- | --------- | --------- | --------- |
| 1 | NULL | 1 | Level 1 |
| 2 | 1 | 1 | Level 2 |
| 3 | 2 | 1 | Level 3 |
| 4 | 3 | 1 | Level 4 |
| 5 | 4 | 1 | Level 5 |
| 6 | 2 | 1 | Level 3.1 |
| 7 | 2 | 1 | Level 3.2 |
Table name: folders_users
| id | folder_id | user_id | owner | read | write |
| 1 | 1 | 1 | 1 | 1 | 1 |
| 2 | 3 | 2 | 0 | 1 | 1 |
So based on record 1 in folder_users, Joe Bloggs should have owner, read & write permissions for all folders underneath Level 1. Joe Bloggs, then gives John Snow read & write access to Level 3, which in turn should give Joe Bloggs read & write access to Level 3, Level 3.1, Level 3.2 and anything created under any of these in future.
Additionally, it should be possible for a user to star a folder. I'd imagine this can simply be achieved with a separate table and query this separately -
Table name: starred_folders
| id | folder_id | user_id |
| -- | --------- | ------- |
| 1 | 7 | 2 |
The current solution I have is for every folder in the chain a user has permission to access, a record is created in the folders_users table. I feel like this is just overcomplicating things and creating excessive numbers of records. This is especially true when it comes to sharing a folder as I have to recreate the entire tree for that one user. Or, imagine if a user revokes write access to one of the shared users, the entire tree (potentially hundreds of records) has to be updated for a single flag.
What would be the best way to generate these trees, and to quickly and efficiently determine the user's access level in any given folder? I suspect the only way to do this is recursion, but I'm concerned about its efficiency? Or, should I perhaps be using something entirely different from MySQL for this? I've had a brief look into graph databases but I can't see it being a way forward for us as we don't have the infrastructure to support it.
Thanks,
Chris.

I'm writing this as a solution not the most efficient one.
You can add a column to your folders table (let's call it access) and then put ids of people that have access to that folder and it's children. I assume when you want to show information about a folder you must get its parents information from table as well so you won't need to add new queries for that.
And if you just have a access definition you can simple add records to this column like user1,user2,... and if not you can serialize an array like this
[
"read" => [user1,user2,...],
"write" => [user2]
]
Of course you can add a column for each access but if you have so many accesses this might be a solution too.

Related

Database model for a multilanguage translation module

I need to design a db model for a backend module where user can translate page content into multiple languages. The things that will be translated are basic words, phrases, link names, titles, field names, field values. They should also be grouped so i can find them by group name. For example if there is a select field on page with different colors as options then i should be able to select all of them by group name.
So here is what i have at the moment:
lang
+----+---------+
| id | name |
+----+---------+
| 1 | english |
| 2 | german |
+----+---------+
lang_entity
+----+------------+-------------+-------+-------+
| id | module | group | name | order |
+----+------------+-------------+-------+-------+
| 1 | general | | hello | 0 |
| 2 | accounting | colorSelect | one | 1 |
| 3 | accounting | colorSelect | two | 2 |
| 4 | accounting | colorSelect | three | 3 |
+----+------------+-------------+-------+-------+
lang_entity_translation
+----+---------+----------------+-------------+
| id | lang_id | lang_entity_id | translation |
+----+---------+----------------+-------------+
| 1 | 1 | 1 | Hello |
| 2 | 2 | 1 | Guten tag |
| 3 | 1 | 2 | One |
| 4 | 2 | 2 | Ein |
| 5 | 1 | 3 | Two |
| 6 | 2 | 3 | Zwei |
| 7 | 1 | 4 | Three |
| 8 | 2 | 4 | Drei |
+----+---------+----------------+-------------+
So lang table holds different languages.
Table lang_entity has entities that can be translated for different languages.
Module row is just to group them by page modules in the backend translating module. Also this gives me possiblity to have entities with same name for different modules.
Group as mentioned is needed for selects and maybe some other places where multiple values are going to be used. This also gives me an option to allow user to add and order entities in one group.
And table lang_entity_translation holds the translations for each entity in each language.
So my question is are visible flaws in this kind of a design? Would you reccomend something different?
Also a bonus question: I really dont like the lang_entity table name, do you have a better idea of a table name that would hold all the words/phrases that are translated? :)
Edit: similar, but not a duplicate. The linked question is about translating dynamic products and having a seperate table for each translated type. Im talking about translating whole page content, including groups in a single table.
I don't understand the order column of lang_entity, but then I probably don't need to.
The setup looks sane, but make sure you add foreign key constraints from lang_entity_translation to language and lang_entity.
As for naming, I would call the table phrase or translatable.
We had similar situation. This was 7 years before.
We had different column for different language. Like for name we had
Name_Eng,Name_Ger,Name_Spa .We had 7-10 language.
We had common id for name for all language.
Based on the Language selection from UI we passed the language code to Back end In the Stored proc it was appended to the column Name
Example, we will be passing "Eng" if English is selected and we form the column name as Name_Eng and fetch the data. we were using dynamic query.

MySQL Accounting Code Database Structure

I have a question on making the effective database structure for accounting code. The result I was expecting is this
| ID | Code | Name | Parent |
| 1 | 1 | Assets | |
| 2 | 1 | Tangible Fixed Assets | 1 |
| 3 | 1 | Building | 2 |
| 4 | 2 | Intangible Fixed Assets| 1 |
| 5 | 1 | CopyRights | 3 |
I've been thinking about making 3 tables such as tbl_lvl1 for main parent, tbl_lvl2 as first child and tbl_lvl3 as second child. I found about recursive query, which is just only using 1 table, but it's kind of difficult making recursive query in MYSQL.
And the result I want to view in PHP, is something like this
| Code | Name |
| 1 | Assets |
| 11 | Tangible Fixed Assets |
| 111 | Building |
| 12 | Intangible Fixed Asset |
| 121 | CopyRights |
Which structure I should make? Using 3 table or 1 table ? Thank you
You're looking for a search tree, and I'd especially suggest a B-tree.
A search tree, generally spoken, allows you to hierarchically search for all sub-nodes in a single query through nested intervals.
There are literally dozens of implementations, so you don't need to dig deep into the details, even though I would suggest it, as it's a major data structure that you should be used to.

Too long loading page - analytics system

I would like to ask you for advice related to my own analytics system.
So far my system collects all the clicks and save them in a SQL database.
First part of analytics.
The SQL database logs looks like this:
+----+----------------------+-------------+---------------------------------------------+----------------+--------------+----------+
| id | time | address | address_to | ip | resolution | id_guest |
|----+----------------------+-------------+---------------------------------------------+----------------+--------------+----------|
| 1 | 2013-12-03#14:31:35 | index.php | https://www.youtube.com/watch?v=6VJBBUqr1wM | 89.XX.XXX.6 | 1366x768 | 6 |
| 2 | 2013-12-03#14:48:21 | file.php | https://www.youtube.com/watch?v=0EWbonj7f18 | 89.XX.XXX.6 | 1366x768 | 6 |
| 3 | 2013-12-03#16:16:55 | contact.php | https://www.youtube.com/watch?v=_o-XIryB2gg | 178.XX.XXX.140 | 1920x1080 | 11 |
| 4 | 2013-12-03#16:21:32 | index.php | https://www.youtube.com/watch?v=z0M96LyTyX4 | 178.XX.XXX.140 | 1920x1080 | 11 |
| 5 | 2013-12-03#16:44:32 | movies.php | https://www.youtube.com/watch?v=cUhPA5qIxDQ | 178.XX.XXX.140 | 1920x1080 | 11 |
+----+----------------------+-------------+---------------------------------------------+----------------+--------------+----------+
Each click is added to the database as a new record.
All movie on my website is on second table in SQL database (movies):
+----+----------------------+-------------+---------------------+
| id | name | address | tags |
|----+----------------------+-------------+---------------------|
| 1 | 2013-12-03#14:31:35 | 6VJBBUqr1wM | bass,electro,trance |
| 2 | 2013-12-03#14:48:21 | 0EWbonj7f18 | electro,house,new |
| 3 | 2013-12-03#16:16:55 | _o-XIryB2gg | electro,party,set |
| 4 | 2013-12-03#16:21:32 | z0M96LyTyX4 | trance,house,new |
| 5 | 2013-12-03#16:44:32 | cUhPA5qIxDQ | techno,new,set |
+----+----------------------+-------------+---------------------+
Everything works flawlessly. In the database I have all the movies viewed by the user, which I want precisely define, so write down the IP + resolution.
First question:
Is this a good method for determining user?
--
Second part of analytics.
Now I want to use the collected logs and display interface with movies based on browsed materials.
I choose all logs from the database for the user who enters the website.
From the logs I choose identifier film and scan it in the table components for take logs and put into an array. For example, a user with ID = 6 will have an array:
array(
[0] = > bass,
[1] = > electro,
[2] = > trance,
[3] = > electro,
[4] = > house,
[5] = > new
);
Now I will sort the contents of the array in order of most frequently occurring:
array(
[2] = > electro,
[1] = > bass,
[1] = > trance,
[1] = > house,
[1] = > new
);
On the basis of the contents of the array can show user videos that might interest him.
Everything worked perfectly, but the problem I discovered only now ...
In the table logs I've had more than 4.5 million records. As you can imagine, searching of such a large number of records takes a lot of time and enter the site sometimes lasts up to 10 seconds...
I hope my poor English is fairly clear.
Please, any advice how to solve this problem with loading page.
Use indexes where needed, hard to tell exactly where - you didnt show any queries - basically you want to have indexed columns in WHERE part of the queries and also in JOINS. You dont have to index column that stays the same most of the time - isloggedin, isadmin, language or so
Make search tables for data you nead to search - for example if you need to know prefered resolution or how many times user had visited a site, you can make a cron job to parse this data for all users and store it in search table. This can be also used to make some statistics if you need it. For those Tags you could have a table with user_id, tag, count
If you need only last visited site, last resolution,... just make a table for that, where you can store and update one row for every user

Database design with undetermined data

Recently I have been planning a system that allows a user to customize and add to a web interface. The app could be compared to a quiz creating system. The problem I'm having is how to design a schema that will allow for "variable" numbers of additions to be made to the application.
The first option that I looked into was just creating an object for the additions and then serializing it and putting it in its own column. The content wouldn't be edited often so writing would be minimal, reads however would be very often. (caching could be used to cut down)
The other option was using something other than mysql or postgresql such as cassandra. I've never used other databases before but would be interested in learning how to use them if they would improve the design of the system.
Any input on the subject would be appreciated.
Thank you.
*edit 29/3/14
Some information on the data being changed. For my idea above of using a serialized object, you could say that in the table I would store the name of the quiz, the number of points the quiz is worth and then a column called quiz data that would store the serialized object containing the information on the questions. So overall the object could look like this:
Questions(Array):{
[1](Object):Question{
Field-type(int):1
Field-title(string):"Whats your gender?"
Options(Array):{"Female", "Male"}
}
[2](Object):Question{
Field-type(int):2
Field-title(string):"Whats your name?"
}
}
The structure could vary of course but generally i would be storing integers to determin the type of field in the quiz and then a field to hold the label for the field and the options (if there are any) for that field.
In this scenario I would advise looking at MongoDB.
However if you want to work with MySQL you can think about the entity-attribute-value model in your design. The EAV model allows you to design for entries that contain a variable number of attributes.
edit
Following your update on the datatypes you would like to store, you could map your design as follows:
+-------------------------------------+
| QuizQuestions |
+----+---------+----------------------+
| id | type_id | question_txt |
+----+---------+----------------------+
| 1 | 1 | What's your gender? |
| 2 | 2 | What's your name? |
+----+---------+----------------------+
+-----------------------------------+
| QuestionTypes |
+----+--------------+---------------+
| id | attribute_id | description |
+----+--------------+---------------+
| 1 | 1 | Single select |
| 2 | 2 | Free text |
+----+--------------+---------------+
+----------------------------+
| QuestionValues |
+----+--------------+--------+
| id | question_id | value |
+----+--------------+--------+
| 1 | 1 | Male |
| 2 | 1 | Female |
+----+--------------+--------+
+-------------------------------+
| QuestionResponses |
+----+--------------+-----------+
| id | question_id | response |
+----+--------------+-----------+
| 1 | 1 | 1 |
| 2 | 2 | Fred |
+----+--------------+-----------+
This would then allow you to dynamically add various different questions (QuizQuestions), of different types (QuestionTypes), and then restrict them with different options (QuestionValues) and store those responses (QuestionResponses).

Heirarchical Sub-Menu - PHP

====================================================================
| menu id | parent_id | menu | Level |
====================================================================
| 1 | 0 | Control Panel | 1 |
====================================================================
| 2 | 1 | Administrative | 2 |
====================================================================
| 3 | 2 | Manage User | 3 |
====================================================================
| 4 | 2 | Manage Role | 3 |
====================================================================
| 5 | 1 | Change Password | 2 |
====================================================================
I have this table to create a sub-menu... the problem is don't know how echo the data and display the echoed data into like this:
- Control Panel
- Administrative
- Manage User
- Manage Role
- Change Password
Does anyone have the answer in PHP? Please I need this solution for my project. Please give me an example. Thank You!
print lines with level number of spaces or tabs before the text
print(getTabs($row['level']).'-'.$row['menu]))
Where getTabs returns argument number of tabs/spaces
Leaving the string format part to you
Your data is already hierarchical - even without the level column, you can build the whole tree. Can you be more specific regarding what you are trying to achieve?

Categories