Database design for different groups access and display results - php

I want to make different roles access to admin dashboard. For now they are superadmin and admin.. later I will add 2 more but for now are just this two. For now superadmin has all permissions include to create new admins. Admin will have permission to create normal users later but for now differences between superadmin and admin is only this. I'm fairly new in PHP and before to post here I found few projects like this but they a quite advanced and I can't understand them well as they are with OOP approach.
So I have made few tables in database: users, action_permissions and user_permissions.
Table users hold data for users.. nothing special:
+---------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+----------------+
| user_id | int(11) | NO | PRI | NULL | auto_increment |
| user_username | varchar(35) | NO | | NULL | |
| user_password | char(64) | NO | | NULL | |
| user_email | varchar(55) | NO | | NULL | |
| user_role | int(11) | NO | | NULL | |
+---------------+-------------+------+-----+---------+----------------+
Table action_permission hold gp_name: Creat(1), Read(2), Update(4), Delete(8)
+---------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+----------------+
| gpid | int(11) | NO | PRI | NULL | auto_increment |
| gp_name | varchar(10) | NO | | NULL | |
| gp_permission | int(4) | NO | | NULL | |
+---------------+-------------+------+-----+---------+----------------+
Table user_permissions hold role_id.user_permissions = users.user_role. In permission_namespace I store SuperAdmin or Admin..and permission_action is gp_permission
+----------------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+-------------+------+-----+---------+----------------+
| permission_id | int(11) | NO | PRI | NULL | auto_increment |
| role_id | int(11) | NO | | NULL | |
| permission_namespace | varchar(50) | NO | | NULL | |
| permission_action | int(4) | NO | | NULL | |
+----------------------+-------------+------+-----+---------+----------------+
So far so good. I have successfully logged in with superadmin and admin and I use this query to see if I get proper permission_action:
$value = $pdo->prepare("SELECT *
FROM users
LEFT JOIN user_permissions ON users.user_role = user_permissions.role_id
WHERE user_role = ?");
$value->bindValue(1, $_SESSION['user_role'], PDO::PARAM_INT);
$value->execute();
foreach($value as $row)
{
....
}
Now I have few questions:
1) Since this wont be some complex system is this approach acceptable i.e. is it correct?
2) Can you provide me with simple example when user login into system how to show stuff that only with his roles can see? Do I need multiple if{}else{} conditions on the page or I can have some class page and include?
Example:
user with permissions of admin login in. He then must see only CRUD for normal users.
user with permissions of superadmin log in. He will see CRUD for admins and normal users.
p.s. Sorry for long post but I'm almost done this.. just need a little help to finish it.

Related

Laravel 8 Many to Many relationship not working (no errors being thrown)

I am new to Laravel but am having a very odd issue.
I successfully created models and migrations for 6 tables to work with many-to-many relationships. One of the relationships works fine and I can retrieve data through a route. The other, however, only returns a white screen (no error, no nothing). The networking tab response says: "This request has no response data available" - but I've triple checked the database and search for different records and it never returns any results.
I've kept the information as simple as possible and followed all naming conventions.
Any idea what is going on here? Is something configured wrong? Please help me from going crazy - I've tried a ton of code iterations and nothing seems to work to establish the many to many client/network relationship.
Not Working Relationship
Client table:
+----------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-----------------+------+-----+---------+----------------+
| id | bigint unsigned | NO | PRI | NULL | auto_increment |
| client_name | varchar(255) | NO | | NULL | |
| client_type_id | bigint unsigned | YES | | NULL | |
| address | varchar(255) | YES | | NULL | |
| address_2 | varchar(255) | YES | | NULL | |
| city | varchar(255) | YES | | NULL | |
| state | varchar(255) | YES | | NULL | |
| zip_code | varchar(255) | YES | | NULL | |
| country | varchar(255) | YES | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+----------------+-----------------+------+-----+---------+----------------+
Network table:
+--------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+-----------------+------+-----+---------+----------------+
| id | bigint unsigned | NO | PRI | NULL | auto_increment |
| network_name | varchar(255) | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+--------------+-----------------+------+-----+---------+----------------+
client_network table:
+------------+-----------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-----------------+------+-----+---------+----------------+
| id | bigint unsigned | NO | PRI | NULL | auto_increment |
| client_id | bigint unsigned | NO | | NULL | |
| network_id | bigint unsigned | NO | | NULL | |
| created_at | timestamp | YES | | NULL | |
| updated_at | timestamp | YES | | NULL | |
+------------+-----------------+------+-----+---------+----------------+
Client model:
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Client extends Model
{
use HasFactory;
public function networks()
{
return $this->belongsToMany(Network::class);
}
}
Network model:
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Network extends Model
{
use HasFactory;
public function clients()
{
return $this->belongsToMany(Client::class);
}
}
Route:
Route::get('/testclient', function() {
$network = Client::find(1)->network_name;
return $network;
});
To anyone dealing with a similar issue, the problem in this situation was not the relationship, it was the route extracting the data from the relationship.
Not Working Route
Route::get('/testclient', function() {
$network = Client::find(1)->network_name;
return $network;
});
Working Route
Route::get('/testing', function(){
$networks = Client::find(1)->networks;
foreach($networks as $network){
$network_arr[] = $network->network_name;
}
return $network_arr;
To solve the issue, the suggested code in the comments helped to show that data was indeed being extracted from the relationship but not displaying properly.
Helper Code:
dd(Network::find(1)->clients);
You make a typo, change
Route::get('/testclient', function() {
$network = Client::find(1)->network;
return $network;
});
To
Route::get('/testclient', function() {
$network = Client::find(1)->networks; //Add s
return $network;
});

How to display information from Two sql tables on my blog using php and html

I am trying to display information from my SQL tables on my web blog. I have two tables blog_posts and blog_members which look like
Blog_members
+----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+------------------+------+-----+---------+----------------+
| memberID | int(11) unsigned | NO | PRI | NULL | auto_increment |
| username | varchar(255) | YES | | NULL | |
| password | varchar(255) | YES | | NULL | |
| email | varchar(255) | YES | | NULL | |
+----------+------------------+------+-----+---------+----------------+
and
blog_posts
+-----------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------------+------+-----+---------+----------------+
| postID | int(11) unsigned | NO | PRI | NULL | auto_increment |
| postTitle | varchar(255) | YES | | NULL | |
| postDesc | text | YES | | NULL | |
| postCont | text | YES | | NULL | |
| postDate | datetime | YES | | NULL | |
+-----------+------------------+------+-----+---------+----------------+
I am able to add the information from one table but I want to display memeberID when I post an article do I need additional columns in the blog_posts table if so how would I go about this would I need to use a join?
I am displaying the information on my blog using the PHP below.
$stmt = $db->query('SELECT postID, postTitle, postDesc, postDate FROM blog_posts ORDER BY postID DESC');
// $stmt = $db->query('SELECT memberID FROM blog_members');
while($row = $stmt->fetch()){
echo '<div>';
echo '<h1>'.$row['postTitle'].'</h1>';
echo '<p>Posted on '.date('jS M Y H:i:s', strtotime($row['postDate'])).'</p>';
// echo '<p> by'.$row['memberID'].'</p>';
echo '<p>'.$row['postDesc'].'</p>';
echo '<p1>Read More</p1>';
echo '</div>';
echo '<hr />';
that displays the posts but not the memberID I would like a post to have the member that created it aswell.
Think about a row in your posts table. How do you know which member that row belongs to? As mentioned in the comments, you can add a memberId to your posts table so that you can join the two tables and find both the member that belongs to a post, as well as the posts for a particular member. One convention for these columns is to prefix them with something like fk (foreign key) to indicate their role on the table. The table might look like this:
+-------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------+------+-----+---------+----------------+
| postID | int(11) unsigned | NO | PRI | NULL | auto_increment |
| postTitle | varchar(255) | YES | | NULL | |
| postDesc | text | YES | | NULL | |
| postCont | text | YES | | NULL | |
| postDate | datetime | YES | | NULL | |
| fkMemberID | int(11) unsigned | NO | | NULL | |
+-------------+------------------+------+-----+---------+----------------+
Then after you have retrieved a post, you will have the memberId that is the owner of that post, and you can retrieve the member details using that id.
(You would also create a separate foreign key object in the database which maintains the integrity of the foreign key columns. i.e. it makes sure you don't put a memberId of say 53 into the posts table fkMemberId column unless there is a memberId of 53 in the member table. You probably already know that but just thought to mention. :) )

Multi table deletion MySQL through PHP

I am making a simple database to use as a mock example for an E-Commerce website. One things that I am required to do it delete records from the database using PHP. I can delete a user name or a order or etc.. But when I try to do them all at once I am screwing something up. What I want to happen is delete all the information about a user. Example : User info, Shipping Info, Billing Info, Credit Card Info, and anything else to do with the specific user. My tables are as followed.
mysql> SHOW columns FROM shirt_billing_addresses;
+----------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------------+--------------+------+-----+---------+----------------+
| shirt_billing_addresses_id | mediumint(9) | NO | PRI | NULL | auto_increment |
| house | mediumint(9) | NO | | NULL | |
| street | varchar(100) | NO | | NULL | |
| city | varchar(100) | NO | | NULL | |
| state | char(2) | NO | | NULL | |
| zip | char(5) | NO | | NULL | |
+----------------------------+--------------+------+-----+---------+----------------+
6 rows in set (0.01 sec)
mysql> SHOW columns FROM shirt_credit_cards;
+-----------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+--------------+------+-----+---------+----------------+
| shirt_credit_cards_id | mediumint(9) | NO | PRI | NULL | auto_increment |
| shirt_users_id | mediumint(9) | NO | MUL | NULL | |
| type | varchar(30) | NO | | NULL | |
| no | char(16) | NO | | NULL | |
| security_code | char(3) | NO | | NULL | |
+-----------------------+--------------+------+-----+---------+----------------+
5 rows in set (0.04 sec)
mysql> SHOW columns FROM shirt_orders;
+-----------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------------+--------------+------+-----+---------+----------------+
| shirt_orders_id | int(11) | NO | PRI | NULL | auto_increment
| order_total | double(6,2) | NO | | NULL | |
| payment_date | datetime | NO | | NULL | |
| shirt_credit_cards_id | mediumint(9) | NO | MUL | NULL | |
| shirt_shipping_addresses_id | mediumint(9) | NO | MUL | NULL
| shirt_billing_addresses_id | mediumint(9) | NO | MUL | NULL |
| shirt_shipping_methods_id | tinyint(4) | NO | MUL | NULL |
+-----------------------------+--------------+------+-----+---------+----------------+
7 rows in set (0.02 sec)
mysql> SHOW columns FROM shirt_shipping_addresses;
+-----------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------------+--------------+------+-----+---------+----------------+
| shirt_shipping_addresses_id | mediumint(9) | NO | PRI | NULL | auto_increment
| house | mediumint(9) | NO | | NULL | |
| street | varchar(100) | NO | | NULL | |
| city | varchar(100) | NO | | NULL | |
| state | char(2) | NO | | NULL | |
| zip | char(5) | NO | | NULL | |
+-----------------------------+--------------+------+-----+---------+----------------+
6 rows in set (0.03 sec)
mysql> SHOW columns FROM shirt_users;
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| shirt_users_id | mediumint(9) | NO | PRI | NULL | auto_increment |
| first_name | varchar(30) | NO | | NULL | |
| last_name | varchar(30) | NO | | NULL | |
| email | varchar(30) | NO | | NULL | |
| user_id | varchar(30) | NO | | NULL | |
| password | char(40) | NO | | NULL | |
+----------------+--------------+------+-----+---------+----------------+
6 rows in set (0.03 sec)
mysql> SHOW columns FROM shirt_users_types;
+----------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------------+--------------+------+-----+---------+----------------+
| shirt_users_types_id | int(11) | NO | PRI | NULL | auto_increment |
| shirt_users_id | mediumint(9) | NO | MUL | NULL | |
| shirt_types_id | smallint(6) | NO | MUL | NULL | |
| shirt_orders_id | int(11) | NO | MUL | NULL | |
| type_quantity | smallint(6) | NO | | NULL | |
| type_total | double(6,2) | NO | | NULL | |
+----------------------+--------------+------+-----+---------+----------------+
6 rows in set (0.03 sec)
The PHP code I have is as follows:
#mysqli_query($link, "SET AUTOCOMMIT=0");
$select_sui = "SELECT
shirt_users.shirt_users_id,
shirt_users_types.shirt_users_types_id,
shirt_orders.shirt_orders_id,
shirt_shipping_addresses.shirt_shipping_addresses_id,
shirt_billing_addresses.shirt_billing_addresses_id,
shirt_credit_cards.shirt_credit_cards_id
from
shirt_users,
shirt_users_types,
shirt_orders,
shirt_shipping_addresses,
shirt_billing_addresses,
shirt_credit_cards
where
shirt_users.shirt_users_id = shirt_users_types.shirt_users_id and
shirt_users_types.shirt_orders_id = shirt_orders.shirt_orders_id and
shirt_orders.shirt_shipping_addresses_id = shirt_shipping_addresses.shirt_shipping_addresses_id and
shirt_orders.shirt_billing_addresses_id = shirt_billing_addresses.shirt_billing_addresses_id and
shirt_orders.shirt_credit_cards_id = shirt_credit_cards.shirt_credit_cards_id and
shirt_users.shirt_users_id = $shirt_users_id";
The only problem is that this query will return an empty set unless all the tables have the correct information in it. This is a problem because if a user has not ordered any items yet but I want to delete them from the database, It will not allow me. Any help on this matter would be greatly appreciated.
You have to use LEFT JOIN to link your tables (MySQL doc).
SELECT [...]
FROM shirt_users
LEFT JOIN shirt_users_types ON shirt_users.shirt_users_id = shirt_users_types.shirt_users_id
LEFT JOIN shirt_orders ON shirt_users_types.shirt_orders_id = shirt_orders.shirt_orders_id
LEFT JOIN shirt_shipping_addresses ON shirt_orders.shirt_shipping_addresses_id = shirt_shipping_addresses.shirt_shipping_addresses_id
LEFT JOIN shirt_billing_addresses ON shirt_orders.shirt_billing_addresses_id = shirt_billing_addresses.shirt_billing_addresses_id
LEFT JOIN shirt_credit_cards ON shirt_orders.shirt_credit_cards_id = shirt_credit_cards.shirt_credit_cards_id
WHERE
shirt_users.shirt_users_id = $shirt_users_id
With this you'll be able to load shirt_users even if there is no linked record in other tables.
EDIT for Delete records
If you want to delete in all your tables in only one request, you must use as syntax which is fairly the same as your first try (by replacing the SELECT statement by DELETE). ANd you will get the same problem (it will only delete records which have a linked record in each table and not null).
Method 1 :
NB : Your DB must support foreign keys (for example, MyISAM doesn't support it but InnoDB does).
You can use this method, if you always need in your app to delete all linked records.
The most beautiful way to achieve this is to add a ON DELETE CASCADE constraint on your relations :
-- Drop the old constraint ("fk_test" must be replace by your constraint name)
ALTER TABLE shirt_users_types DROP FOREIGN KEY `fk_test`;
-- Create the new with ON DELETE
ALTER TABLE shirt_users_types
ADD CONSTRAINT `fk_test`
FOREIGN KEY (`shirt_users_id` )
REFERENCES `shirt_users` (`shirt_users_id` )
ON DELETE CASCADE;
When you will delete a shirt_users entry, all linked records in shirt_users_types.
In a few words, with ON DELETE CASCADE, each time you will delete a parent element (the One side of the relation), all the children (the Many side of the relation or the table wich contains the column <related_record>_id) will be automatically deleted too.
Method 2 :
If your relation can be nullable (for example if shirt_users_types.shirt_orders_id can be null) or if your DB schema not permits to delete all needed records with ON DELETE CASCADE.
You can delete your records with several DELETE using relations to shirt_users in order to retrieve records in each linked table. (For correct syntax of DELETE with relations see MySQL docs).
In your code your unique identifier is $shirt_users_id, so you have to start by deleting in the tables which are not directly linked to shirt_users and by reaching back your relations to finish by the table shirt_users.
If you don't delete records in the correct order, you will not be able to delete all you need.
For example, with schema described in your question, if you delete records in shirt_orders before shirt_shipping_addresses, you will no longer can retrieve shipping addresses with $shirt_users_id because shirt_shipping_addresses is linked to shirt_users due to its relation with shirt_orders.
So the correct order for your current schema is (there is no need to have an order for table on the same line the list above) :
shirt_shipping_addresses / shirt_billing_addresses
shirt_orders / shirt_credit_cards / shirt_users_types
shirt_users
You can have a contraint fails error due to foreign keys. In this case you can make your delete in a TRANSACTION (it's a best practice) or disable foreign key checks for your queries.
SET foreign_key_checks = 0;

Trying to query two tables and I can't figure out how

The first is the place table where the general information is kept and the second is the wait table where users sign up (like a waiting list)
+---------+--------------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(30) | YES | | NULL | |
| userid | int(3) | YES | | NULL | |
| address | varchar(300) | YES | | NULL | |
| desc | varchar(550) | YES | | NULL | |
| phone | int(15) | YES | | NULL | |
| image | varchar(50) | YES | | NULL | |
| website | varchar(100) | YES | | NULL | |
| cat | varchar(25) | YES | | NULL | |
| date | timestamp | NO | | CURRENT_TIMESTAMP | |
+---------+--------------+------+-----+-------------------+----------------+
+----------+-----------+------+-----+-------------------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-----------+------+-----+-------------------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| userid | int(11) | YES | | NULL | |
| place_id | int(11) | YES | | NULL | |
| date | timestamp | NO | | CURRENT_TIMESTAMP | |
+----------+-----------+------+-----+-------------------+----------------+
For now I m doing a SELECT * FROM place; and displaying the data on the home page. Something like tihs:
<? foreach($places as $place): ?>
<? echo $place->name; ?>; <? echo $place->userid; ?> etc ...
Click this to insert your userid and $place->id into wait table
<? endforeach ?>
This is where I got lost. I would like to do something like:
<? if($current_user_id == $userid_from_wait_that_matches_place_id): ?>
<p>You already registered for this!</p>
<? else: ?>
Click this to insert your userid and $place->id into wait table
<? endif; ?>
Not sure if it's better to check for the user's id in the model that adds data to the wait table or to check in the model that grabs data for the home page. From what I've read, the second option would be better. Or should I use two separate queries ?
I think your database design is wrong: you should create seperate users table with user-specific data (name, image,...) plus an user_id. And an another table with "general" information (as you said): name, desc, map, etc. And in this table doesn't use user-specific information only user_id.
And if your database isn't too large you can use a select tag with valid user_ids so you don't need validation.
EDIT if you want to know what are the user_ids which isn't in wait table, use similar query:
SELECT user.userid
FROM user
LEFT JOIN wait ON user.userid=wait.userid
WHERE ISNULL(wait.place_id)
These userid can put into a select-list.
Please read up on joins in select queries. Looks like you need to use a left outer join between your master table and your temporary table: http://www.tizag.com/mysqlTutorial/mysqlleftjoin.php.
You could use a query like this one:
select *
from wait_table
left join general_info_table on wait_table.user_id = general_info_table.user_id
where wait_table.user_id = 1;
This way, IF the user_id is in the wait_table it would return you the info on the client... if it doesn't exists in the table, well, should return null.
I would filter out which table fields i really need from the query, though.

Displaying results as hyperlinks to an additional query?

I've been looking for a way to display MySQL results as hyperlinks that will perform another query when clicked.
Say I have a 2 tables in my database linked by ALTER TABLE topics ADD FOREIGN KEY(topic_cat) REFERENCES categories(cat_id) ON DELETE CASCADE ON UPDATE CASCADE;
categories:
+-----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+--------------+------+-----+---------+----------------+
| cat_id | int(8) | NO | PRI | NULL | auto_increment |
| cat_name | varchar(255) | NO | UNI | NULL | |
| cat_description | varchar(255) | NO | | NULL | |
+-----------------+--------------+------+-----+---------+----------------+
topics:
+---------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------+------+-----+---------+----------------+
| topic_id | int(8) | NO | PRI | NULL | auto_increment |
| topic_subject | varchar(255) | NO | | NULL | |
| topic_date | datetime | NO | | NULL | |
| topic_cat | int(8) | NO | MUL | NULL | |
| topic_by | int(8) | NO | MUL | NULL | |
+---------------+--------------+------+-----+---------+----------------+
I also have a PHP search form that queries the table "categories":
$var = #$_GET['search'] ;
$trimmed = trim($var);
$query = "select * from categories where cat_name like \"%$trimmed%\"
order by cat_name";
I want to be able to display the results of the above query as hyperlinks and when clicked, I want to display results that are linked to "cat_name" by "topic_cat" and "cat_id". I can't seem to find an example of this anywhere online. Any suggestions?
When you output results, do
print ("<a href='show_category_topics.php?id=".$row["cat_id"]."'>".$row["cat_name"]."</a>");
On a new page (show_category_topics.php) run the query
$query = "SELECT * FROM topics WHERE topic_cat ='".mysql_real_escape_string($_GET["id"])."'"; and print results.
You can also use ajax calls to php pages.

Categories