Admittedly, I'm not very strong with PHP. I have a situation where I need to check if a given user is authorized to visit a given webpage.
A user can potentially have zero or more roles. To do this, I setup three tables:
- User
- Role
- UserRole
Role references user. UserRole references both User and Role.
A webpage is will always have a parent menu to reference. Think of it in the case of a dropdown menu where the top-level menu item does not represent a webpage, but the child menu items do.
To do this, I setup 3 tables:
- Menu
- MenuItem
- MenuItemRole
Menu and MenuItem both have a name. MenuItem references Menu. And MenuItemRole references both Menu and MenuItem.
Now I created a function that has the following arguments: menuName, menuItemName, and userId.
As far as my business logic is concerned, I am doing the following:
- get all of the user's roles by the given userId
- get the menu id by the given menuName
- get the menu item id by the given menuItemName
- get all of the menu item roles by the results of #2 and #3
- loop over all the records from #1 and #4 until a match is found
This is the code that I setup:
public static function authorizeView($menuName, $menuItemName, $userId) {
if (!$menuName || $menuItemName || $userId) {
throw new Exception("Unauthorized");
}
// get all of the user's roles
$filters = array();
$filters["UserId"] = $userId;
$userRoles = UserRoleService::query($filters);
if ($userRoles["total"] === 0) {
return false;
}
// get the menu id
$filters = array();
$filters["MenuName"] = $menuName;
$menus = MenuService::query($filters);
if ($menus["total"] === 0) {
return false;
}
$menuId = $menus["records"][0]["MenuId"];
// get the menu item id
$filters = array();
$filters["MenuItemName"] = $menuItemName;
$menuItems = MenuItemService::query($filters);
if ($menuItems["total"] === 0) {
return false;
}
$menuItemId = $menuItems["records"][0]["MenuItemId"];
// get the menu item roles by the menu/menu item
$filters = array();
$filters["MenuId"] = $menuId;
$filters["MenuItemId"] = $menuItemId;
$menuItemRoles = MenuItemRoleService::query($filters);
if ($menuItemRoles["total"] === 0) {
return false;
}
// loop over all of the user roles, if one of the values is in one of the menu item roles, return true
$authorized = false;
foreach ($userRoles as $userRole) {
foreach ($menuItemRoles as $menuItemRole) {
if ($userRole["RoleId"] === $menuItemRole["RoleId"]) {
$authorized = true;
break;
}
}
if ($authorized) {
break;
}
}
// otherwise return false
return $authorized;
}
My first question is: am I architect-ing this right or is it over-engineered?
My second question is: can the nested loop be optimized for readability? Up to that point, I can read everything pretty clearly, but that one section sticks out at me because it is not immediately clear what it is doing.