views:

118

answers:

4

Hello,

I'm currently rebuilding an admin application and looking for your recommendations for best-practice! Excuse me if I don't have the right terminology, but how should I go about the following?

Take the example of "users" - typically we can create a class with properties like 'name', 'username', 'password', etc. and make some methods like getUser($user_ID), getAllUsers(), etc. In the end, we end up with an array/arrays of name-value pairs like; array('name' => 'Joe Bloggs', 'username' => 'joe_90', 'password' => '123456', etc).

The problem is that I want this object to know more about each of its properties.

Consider "username" - in addition to knowing its value, I want the object to know things like; which text label should display beside the control on the form, which regex I should use when validating, what error message is appropriate? These things seem to belong in the model.

The more I work on the problem, the more I see other things too; which HTML element should be used to display this property, what are minimum/maximum values for properties like 'registration_date'?

I envisaged the class looking something like this (simplified):

class User {
    ...etc...
    private static $model = array();
    ...etc...
    function __construct(){
        ...etc...
        $this->model['username']['value'] = NULL; // A default value used for new objects.
        $this->model['username']['label'] = dictionary::lookup('username'); // Displayed on the HTML form. Actual string comes from a translation database.
        $this->model['username']['regex'] = '/^[0-9a-z_]{4,64}$/i'; // Used for both client-side validation and backend validation/sanitising;
        $this->model['username']['HTML'] = 'text'; // Which type of HTML control should be used to interact with this property.
        ...etc...
        $this->model['registration_date']['value'] = 'now'; // Default value
        $this->model['registration_date']['label'] = dictionary::lookup('registration_date');
        $this->model['registration_date']['minimum'] = '2007-06-05'; // These values could be set by a permissions/override object.
        $this->model['registration_date']['maximum'] = '+1 week';
        $this->model['registration_date']['HTML'] = 'datepicker';
        ...etc...
    }
    ...etc...
    function getUser($user_ID){
        ...etc...
        // getUser pulls the real data from the database and overwrites the default value for that property.
        return $this->model;
    }
}

Basically, I want this info to be in one location so that I don't have to duplicate code for HTML markup, validation routines, etc. The idea is that I can feed a user array into an HTML form helper and have it automatically create the form, controls and JavaScript validation.

I could then use the same object in the backend with a generic set($data = array(), $model = array()) method to avoid having individual methods like setUsername($username), setRegistrationDate($registration_date), etc...

  • Does this seem like a sensible approach?
  • What would you call value, label, regex, etc? Properties of properties? Attributes?
  • Using $this->model in getUser() means that the object model is overwritten, whereas it would be nicer to keep the model as a prototype and have getUser() inherit the properties.
  • Am I missing some industry-standard way of doing this? (I have been through all the frameworks - example models are always lacking!!!)
  • How does it scale when, for example, I want to display user types with a SELECT with values from another model?

Thanks!

Update

I've since learned that Java has class annotations - http://en.wikipedia.org/wiki/Java_annotations - which seem to be more or less what I was asking. I found this post - http://interfacelab.com/metadataattributes-in-php - does anyone have any insight into programming like this?

A: 

You're running into the Model-View-Controller (MVC) architecture.

The M only stores data. No display information, just typed key-value pairs.

The C handles the logic of manipulating this information. It changes the M in response to user input.

The V is the part which handles displaying things. It should be something like Smarty templates rather than a huge amount of raw PHP for generating HTML.

Having it all "in one place" is the wrong approach. You won't have duplicated code with MVC - each part is a distinct step. This improves code reuse, readability, and maintainability.

Borealid
Some critiques: 1) A model can be more complex than just key-value pairs. 2) Views don't have to be created with a template framework, per se. There is nothing wrong with using 'raw' PHP to generate HTML. The restriction is that the only logic which exists in the views should be related to displaying data. Views should generally be relatively 'simple.' 3) The controller will do more than just make changes to the model. It will also likely determines which views are appropriate and it may need to call other controllers.
George Marian
@Borealid - I tried to keep my problem pattern-agnostic. I tend to find that MVC is actually a bit of a simplification when it comes to web apps - MVVP is the closest pattern to what I'm aiming for at the moment, but again, isn't it a desktop paradigm and not necessarily meant for the web?
boatingcow
+3  A: 

You're on the right track there. When it comes to models I think there are many approaches, and the "correct" one usually depends on your type of application.

Your model can be directly an Active Record, maybe a table row data gateway or a "POPO", plain old PHP object (in other words, a class that doesn't implement any specific pattern).

Whichever you decide works best for you, things like validation etc. can be put into the model class. You should be able to work with your users as User objects, not as associative arrays - that is the main thing.

Does this seem like a sensible approach

Yes, besides the form label thing. It's probably best to have a separate source for data such as form labels, because you may eventually want to be able to localize them. Also, the label isn't actually related to the user object - it's related to displaying a form.

How I would approach this (suggestion)

I would have a User object which represents a single user. It should be possible to create an empty user or create it from an array (so that it's easy to create one from a database result for example). The user object should also be able to validate itself, for example, you could give it a method "isValid", which when called will check all values for validity.

I would additionally have a user repository class (or perhaps just some static methods on the User class) which could be used to fetch users from the database and store them back. This repository would directly return user objects when fetching, and accept user objects as parameters for saving.

As to what comes to forms, you could probably have a form class which takes a user object. It could then automatically get values from the user and use it to validate itself as well.

I have written on this topic a bit here: http://codeutopia.net/blog/2009/02/28/creating-a-simple-abstract-model-to-reduce-boilerplate-code/ and also some other posts linked in the end of that one.

Hope this helps. I'd just like to remind that my approach is not perfect either =)

Jani Hartikainen
@Jani - I agree with your approach entirely and it's pretty much what I have already. I wasn't too sure about where the property attributes should live, or where the default values should come from (the model or a database entry?). The form labels currently live in an object fed from a lookup translation database which is then accessed through a lookup() static method - did you see a shortcoming?
boatingcow
...another question is, if I use a method like isValid(), then that method has to know, for example, what the regex is for a username. The same regex is used in the tooltip in the form, the jQuery validator, my POST validator, my db schema description and any help documentation I produce. Where is the best place to store the regex?
boatingcow
If you need to share parts of your validation more, you could create separate validator classes to perform such duties as validating a username. Default values for the properties could be directly in the object itself as defaults - otherwise the values in them can be loaded from anywhere you want. In a typical case, you would load the values from a form when creating new objects, or from the database when loading existing ones.
Jani Hartikainen
+2  A: 

An abstract response for you which quite possibly won't help at all, but I'm happy to get the down votes as it's worth saying :)

You're dealing with two different models here, in some world we call these Class and Instance, in other's we talk of Classes and Individuals, and in other worlds we make distinctions between A-Box and T-Box statements.

You are dealing with two sets of data here, I'll write them out in plain text:

User a Class .
username a Property;
  domain User;
  range String .
registration_date a Property;
  domain User;
  range Date .

this is your Class data, T-Box statements, Blueprints, how you describe the universe that is your application - this is not the description of the 'things' in your universe, rather you use this to describe the things in your universe, your instance data.. so you then have:

user1 a User ;
  username "bob";
  registration_date "2010-07-02" .

which is your Instance, Individual, A-Box data, the things in your universe.

You may notice here, that all the other things you are wondering how to do, validation, adding labels to properties and so forth, all come under the first grouping, things that describe your universe, not the things in it. So that's where you'd want to add it.. again in plain text..

username a Property;
  domain User;
  range String;
  title "Username";
  validation [ type Regex; value '/^[0-9a-z_]{4,64}$/i' ] .

The point in all this, is to help you analyse the other answers you get - you'll notice that in your suggestion you munged these two distinct sets of data together, and in a way it's a good thing - from this hopefully you can see that typically the classes in PHP take on the role of Classes (unsurprisingly) and each object (or instance of a class) holds the individual instance data - however you've started to merge these two parts of your universe together to try and make one big reusable set of classes outside of the PHP language constructs that are provided.

From here you have two paths, you can either fold in to line and follow the language structure to make your code semi reusable and follow suggested patterns like MVC (which if you haven't done, would do you good) - or you can head in to a cutting edge world where these worlds are described and we build frameworks to understand the data about our universes and the things in it, but it's an abstract place where at the minute it's hard to be productive, though in the long term is the path to the future.

Regardless, I hope that in some way that helps you to get a grip of the other responses.

All the best!

nathan
Good stuff but I'm worried this is a little bit over someone's head who is just beginning :) Esp. since your examples are in Erlang or something
Jani Hartikainen
@Jani totally agree Jani, sometimes you write the answers as much for yourself as the person asking the question though - examples are simply simplified RDF / EAV btw - I'm v interested to see if this over the head or not, in one respect I'd argue that mentioning Active Records and POPOs is over the head, but in reality I'd suggest this is over the head - we'll see - consider this answer an augmentation of the others, including yours :D
nathan
It will probably serve the same purpose as I had in mind when mentioning those patterns - to open some new doors for learning more =)
Jani Hartikainen
@nathan, @Jani; I hadn't come across Abox and Tbox descriptions before - I'm always open to new stuff(especially since my background isn't computer science). I had put the attributes of the model in the __construct as I felt they were common to every object instance. I had put some default values in there too, as didn't see where else they belong. Is it that the typical use of a class is pretty much only name-value, whereas I'm looking for an EAV implementation, hence the multi-dimensional array?
boatingcow
@boatingcow Typically for what you describe, each type of thing (say User) would have it's own Class (in this case a POPO as pointed out by Jani) that class would have properties (username etc) and each property would have a getter and setter, the setter would do the validation. Labels and HTML would be completely abstracted out of the application and placed somewhere in the presentation tier, or 'view' in mvc (so that multiple languages and multiple front ends can be created easily and to separate cross cutting concerns).
nathan
@boatingcow EAV, RDF, triples all come under knowledge representation and is a completely different way of doing things where rather than making a class, you describe one, then you describe 'things' (each user) using the properties from the class description - this is language independent, webized and has lots of goodness, but I'd strongly recommend getting going with MVC, and if your interested in going further do some reading on design pattens, architectures and paradigms then follow your nose from there (long process)
nathan
@nathan, thanks for your comments. If the attributes of the property other than 'value' are abstracted out, where would they go? See the comment in reply to @Jani above.
boatingcow
+1  A: 

Having looked at your question, the answers and your responses; I might be able to help a bit more here (although it's difficult to cover everything in a single answer).

I can see what you are looking to do here, and in all honesty this is how most frameworks start out; making a set of classes to handle everything, then as they are made more reusable they often hit on tried and tested patterns until finally ending up with what I'd say is 'just another framework', they all do pretty much the same thing, in pretty much the same ways, and aim to be as reusable as they can - generally about the only difference between them is coding styles and quality - what they do is pretty much the same for all.

I believe you're hitting on a bit of anti-pattern in your design here, to explain.. You are focussed on making a big chunk of code reusable, the validation, the presentation and so forth - but what you're actually doing (and of course no offence) is making the working code of the application very domain specific, not only that but the design you illustrate will make it almost impossible to extend, to change layers (like make a mobile version), to swap techs (like swap db vendors) and further still, because you've got presentation and application (and data) tiers mixed together, any designer who hit's the app will have to be working in, and changing, your application code - hit on a time when you have two versions of the app and you've got a big messy problem tbh.

As with most programming problems, you can solve this by doing three things:

  1. designing a domain model.
  2. specifying and designing interfaces rather that worrying about the implementation.
  3. separating cross cutting concerns

Designing a domain model is a very important part of Class based OO programming, if you've never done it before then now is the ideal time, it doesn't matter whether you do this in a modelling language like UML or just in plain text, the idea is to define all the Entities in your Domain, it's easy to slip in to writing a book when discussing this, but let's keep it simple. Your domain model comprises all the Entities in your application's domain, each Entity is a thing, think User, Address, Article, Product and so forth, each Entity is typically defined as a Class (which is the blueprint of that entity) and each Class has Properties (like username, register_date etc).

Class User {
  public $username;
  public $register_date;
}

Often we may keep these as POPOs, however they are often better thought of as Transfer Objects (often called Data Transfer Objects, Value Objects) - a simple Class blueprint for an entity in your domain - normally we try to keep these portable as well, so that they can be implemented in any language, passed between apps, serialized and sent to other apps and similar - this isn't a must, indeed nothing is a must - but it does touch on separation of concerns in that it would normally be naked, implying no functionality, just a blueprint ot hold values. Contrast sharply with Business Objects and Utility Classes that actually 'do' things, are implementations of functionality, not just simple value holders.

Don't be fooled though, both Inheritance and Composition also play their part in domain model, a User may have several Addresses, each Address may be the address of several different Users. A BillingAddress may extend a normal Address and add in additional properties and so forth. (aside: what is a User? do you have a User? do you have a Person with 1-* UserAccounts?).

After you've got your domain model, the next step is usually mapping that up to some form of persistence layer (normally a database) two common ways of doing this (in well defined way) are by using an ORM (such as doctrine, which is in symphony if i remember correctly), and the other way is to use DAO pattern - I'll leave that part there, but typically this is a distinct part of the system, DAO layers have the advantage in that you specify all the methods available to work with the persistence layer for each Entity, whilst keeping the implementation abstracted, thus you can swap database vendors without changing the application code (or business rules as many say).

I'm going to head in to a grey area with the next example, as mentioned earlier Transfer Objects (our entities) are typically naked objects, but they are also often a good place to strap on other functionality, you'll see what I mean.

To illustrate Interfaces, you could simply define an Interface for all your Entities which is something like this:

Interface Validatable {
  function isValid();
}

then each of your entities can implement this with their own custom validation routine:

Class User implements Validatable {
  public function isValid()
  {
    // custom validation here
    return $boolean;
  }
}

Now you don't need to worry about creating some convoluted way of validating objects, you can simply call isValid() on any entity and find out if it's valid or not.

The most important thing to note is that by defining the interface, we've separated some of the concerns, in that no other part of the application needs to do anything to validate an object, all they need to know is that it's Validatable and to call the isValid() method.

However, we have crossed some concerns in that each object (instance of a Class) now carries it's own validation rules and model. It may make sense to abstract this out, one easy way of doing this is to make the validation method static, so you could define:

Class User {
  public static function validate(User $user)
  {
    // custom validation here
    return $boolean;
  }
}

Or you could move to using getters and setters, this is another very common pattern where you can hide the validation inside the setter, thus ensuring that each property always holds valid data (or null, or default value).

Or perhaps you move the validation in to it's own library? Class Validate with it's own methods, or maybe you just pop it in the DAO layer because you only care about checking something when you save it, or maybe you need to validate when you receive data and when you persist it - how you end up doing it is your call and there is no 'best way'.

The third consideration, which I've already touched on, is separation of concerns - should a persistence layer care how the things it's persisting are presented? should the business logic care about how things are presented? should an Entity care where and how it's displayed? or should the presentation layer care how things are presented? Similarly, we can ask is there only ever going to be one presentation layer? in one language? What about how a label appears in a sentence, sure singular User and Address makes sense, but you can't simply +s to show the lists because Users is right but Addresss is wrong ;) - also we have working considerations like do I want a new designer having to change application code just to change the presentation of 'user account' to 'User Account', even do I want to change my app code in the classes when a that change is asked for?

Finally, and just to throw everything I've said - you have to ask yourself, what's the job I'm trying to do here? am I building a big reusable application with potentially many developers and a long life cycle here - or would a simple php script for each view and action suffice (one that reads $_GET/$_POST, validates, saves to db then displays what it should or redirects where it should) - in many, if not all cases this is all that's needed.

Remember, PHP is made to be invoked when a request is made to a web server, then send back a response [end] that's it, what happens between then is your domain, your job, the client and user typically doesn't care, and you can sum up what you're trying to do this simply: build a script to respond to that request as quickly as possible, with the expected results. That's and it needn't be any more complicated than that.

To be blunt, doing everything I mentioned and more is a great thing to do, you'll learn loads, understand your job better etc, but if you just want to get the job out the door and have easy to maintain simple code in the end, just build one script per view, and one per action, with the odd reusable bit (like a http handler, a db class, an email class etc).

nathan
@nathan, I really appreciate the time you've taken to write your replies. The above would make a great intro to an article like "why use a framework". However, just thinking about that username regex, since I want the isValid() method, the jQuery validate() routine, the sprintf/l10n HTML tooltip, the phpdoc documentation, and potentially a db schema generator all to quote it, it made sense that it should live in one maintainable location. Domain 'myApp' >> class 'User' >> property 'username' >> attribute 'regex' - is that the wrong place? The regex isn't just for validation...
boatingcow
...to add to the confusion, some attributes might be different for different users and could come from a permissions table when creating the model instance; a super user might have no limit to editing ['registration_date'] as I wrote in the example above, but a reception user might be limited to specific dates with ['registration_date']['minimum'] and ['registration_date']['maximum'].
boatingcow
@boatingcow again you're getting different kinds of things mixed up, domainmodel and property validation is 'is this a valid username', 'is this a valid date' - what you are talking about is business rules, 'if logged in user has role of reception-user then check registration date within bounds(x,y)' two totally different things that have different places in the app - honestly, forget the structure you've came up with, write it all in code in classes and methods with if's then refactor from there ;)
nathan
I apologise for what has obviously become an exasperating point! Can you recommend where the username regex should live? Or, should it be repeated in more than one location?
boatingcow
not at all, and sorry if I conveyed that - username regex, ideally in a constant on the User class (imho), and validation either in a setter, via an isValid method or a User::validate($user) method
nathan