1

I have two objects, one is a User object and the other is a Content object. The User object has properties like 'reads_articles' or 'reads_books'. The Content object has properties like 'is_article' or 'is_book'. If the two properties match up (i.e. the User reads_articles and the Content is_article), then the User can see the Content. Basically, I am looking for the best solution to approach this without using a switch() statement.

I am also wondering if maybe I'm making this too difficult and/or a design pattern is overkill here. Preliminary research has suggested that the Strategy pattern may be the most appropriate, but I'm having a hard time modeling this problem to that pattern. I don't want to hard code the tests - I would really like to come up with a general solution that examines the properties of both classes, and determines whether the Content can be viewed by that User.

Clarification: My end goal is an algorithm that will examine the properties of the User and the properties of the Content, and see if they match. Basically, I want to see if the two objects have matching properties (without hardcoding the properties themselves into the algorithm) and do some actions.

Not sure if what I want is a pipe dream, so I thought I would ask. Thanks for the help!

  • best solution to approach this to do what? Are you trying to dump everything the User can see? Are you trying to check if a User has access to a given Content? Both? Something else? How many types of Content are there? Can they be added/removed, or is this set in the code? – Izkata Dec 23 '13 at 22:33
  • Izkata - I added some clarification to my question. If a property is added to the Content, a corresponding property needs to be added to the User (i.e., a permission property relating to the Content Property) – JRizz Dec 23 '13 at 22:44
1

Basically, I want to see if the two objects have matching properties (without hardcoding the properties themselves into the algorithm) and do some actions.

Yes, this helps. The assumption-based answer I started on wouldn't have sufficed. But KISS - keep it stupid simple - still applies here, IMO. Keep it short and to the point, no need to force it into a design pattern if it doesn't naturally fit.

Lets use a special language feature to our advantage. PHP allows you to call dynamic access to attributes, with names stored as strings in variables. It's often frowned upon in PHP, but is also sometimes the simplest solution. For example, in this pseudocode:

$attribs = array(
   array('User' => 'reads_articles', 'Content' => 'is_article'),
   array('User' => 'reads_books', 'Content' => 'is_book'),
);

class User {
   function can_access(Content) {
      foreach ($attribs as $reqs) {
         if (!property_exists(self, $reqs['User'])) {
            throw Exception("User is missing {$reqs['User']}");
         }
         if (!property_exists(Content, $reqs['Content'])) {
            throw Exception("Content is missing {$reqs['Content']}");
         }
         if (self->{$reqs['User']} && Content->{$reqs['Content']}) {
            return true;
         }
      }
      return false;
   }

   reads_articles = true;
   reads_books = true;
}

(Warning: Untested, and I know I have some of the syntax wrong (such as not actually naming the Content argument, and I doubt the scope is right).)

There's 3 possible sources for $attribs that I can think of. Here's some further thoughts on each:

  • Hardcoded in the source, as above
    • Advantages: It's right there. For maintenance, very easy to see where it's coming from. Even in another file, you don't have to mentally switch context to get it.
    • Disadvantages: It's static. All the User and Content classes have to define every single one, unless can_access is modified to accept that "missing" means "false".
  • Loaded from a database table
    • Advantages: Simply put, it's a lot more flexible and easier to extend in the future since the interface to use the database is already there.
    • Disadvantages: Reading the code, you now have to also look at the database to figure out which attributes will be used together. It's also easier to overlook when adding a new one.
  • Inferred through reflection, examining the class(es)
    • Advantages: New attributes are automatically included, and the "rules" for determining which ones go together are pretty straightforward.
    • Disadvantages: The rules for pairing attributes could just be skin-deep (is_letter / sends_letters does not fit, for example), and there's a chance they can match something they're not supposed to anyway. "Meta" programming like this is also less obvious than the other two, since it tends to be used less often, so it should be documented pretty well.

The disadvantages to using a database are so small that that's the way I'd go with it, unless I was very sure this set of attributes was unlikely to change or the project doesn't already have a database - then I might go with the first option, hardcoding somewhere. At least with it already in a semi-tabulated form like that, it would be simple to convert to a database table if needed.

Inferring it through pattern matching has to be really well-defined, so I'd avoid that as much as possible. It IMO falls into the "neat trick" category that's rarely more useful than other methods, do to the possibility of false positives and likely needing extra documentation.

Now, there is a halfway option that I think should be used instead of attributes directly on the class:

class User {
   $permissions = array(
      'reads_articles' => true,
      'reads_books' => true,
   );
}

This way, there's no overlap with the normal User attributes, and it's obvious which ones are important. The caveats about how to pair these with the ones on Content still applies, so one of the former 3 methods would still be necessary. However, from the question, I'm not sure if this would work for what you have in mind.

(Side note, since it may not be obvious - I'm trying to keep this slightly generic, since I'm not sure if the permissions you speak of are at the class level or instance level. Almost all of the above can be done either way.)

  • I understand the idea. I'm thinking something along the same lines - basically writing an algorithm that can examine the objects for matching properties (i.e. the name based reflection you included). I am thinking something close to this idea is the road I need to go down. Upvoted. I want to let this question sit for a bit before I mark this the answer. – JRizz Dec 23 '13 at 22:58
  • @J.Robertson Two issues I have with loading it through reflection (that I didn't think of until now) are: 1) Determining which attributes are paired with what. Right now it looks simple (is_* -> reads_*s) but relies on a pattern that might not be true. 2) Attributes or methods that coincidentally match the pattern, but aren't supposed to be part of this system. So I would prefer using a database or hardcoding this. – Izkata Dec 24 '13 at 0:15
  • @J.Robertson I expanded on my comment above, then realized there's an IMO better way than attributes directly on User and Content – Izkata Dec 24 '13 at 21:50
  • thanks for the very robust response! All things considered, I think that hardcoding this is, unfortunately, the best way to go. The current system is designed without including a database, and I'm loathe to include it just for this functionality. As for determining the attributes by reflection, I agree that it's too prone for error, and non-intuitive enough that it may add unneeded complexity. I'm fine with hard-coding everything, since I did my due diligence in exploring my options. Thank you for your very informative answer! – JRizz Dec 26 '13 at 14:31
1

You can check to see if an item is a member of a set by using in_array.

So, for example, if your user reads articles and books, the user object's "reads these things" array will contain 'articles' and 'books' (or their coded numerical equivalent), and you can have a method on the user object called ReadsContent() that accepts a content type as a parameter and returns true or false based on whether that content type is in_array.

  • This answer made me think that I could design this in such a way that an array is compiled automatically from the properties of the Content Object, and can use that to check against the User object. One of my goals is to keep from hardcoding the properties inside the algorithm that handles the permissions checking. – JRizz Dec 23 '13 at 22:46
  • If you're using an ORM, you might even get the array handed to you automatically. – Robert Harvey Dec 23 '13 at 22:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.