Reputation: 6111
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:
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 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:
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.
Upvotes: 1
Views: 407
Reputation: 2691
About the first question: It is difficult for me to judge if your architecture is absolutely right or not. As that depends on other logic also related to role/menu (if any). But basically I find it quite similar to the architectures I usually come across, so I don't think there's a problem here.
About the second question: Your nested loop can be rewritten as taking the intersection of 2 arrays. Then, the logic would be: "if the user has at least one role associated with the menu and menu item, he will be authorized". I think that's a bit easier to read.
I rewrote your code (from line // loop over all...
) following the above approach:
$userRoleIds = array_column($userRole, "RoleId");
$menuItemRoleIds = array_column($menuItemRoles, "RoleId");
return !empty(array_intersect($userRoleIds, $menuItemRoleIds));
Upvotes: 1