I'm currently creating my own PHP mvc for a site that I run so that it will include just the code needed yo be as light and fast as possible.
The site has quite a large range of functions and user features so working out which actions go where in which models controlled by which controllers is starting to get quite complex.
Example
Say I have the following member features
Favourites
Friends
History
Each of those can be controlled by the membercontroller but then my question is whether to have them all inside one model or a model for each.
Each of those three has sub many actions such as:
Add to favourites
Remove favourites
Show favourites
Add to history
Remove history
Show history
Add as friend
Remove friend
Message friend
...etc
At the moment I'm thinking a model for each (favourite, friends, history) is probably the best way, but can it get to a point where you have too many models?
At the moment the whole site has 6 controllers, 17 models and 25 views
Yes you can technically have too many models, there is a limit (as always) of how many classes can exist in PHP. But it's pretty large, so keep on going. You can not only have many but also different kind of models at once. So keep on going, don't restrain your coding by thinking there might be a limit you don't see so far.
So not the count of files, but how nicely written your code is, e.g. is everything grouped properly that belongs together? See as well Domain Model.
I suggest you let ModelController deal with actions that somehow modify Model.
I.e. FavoritesController deals with adding, removing and showing favorites (stored on FavoritesModel). Keeps your controllers lean/slim, is a lot easier to test (less methods) and keeps logical app parts together.
Also, you could divide the application into smaller apps that deal each with:
Auth/Login
Social/Sharing
add/read/show articles (main app)
In such scenarios there is no "right" answer, so all I can give you is my own interpretation. I would use a service to do bind one or more models together. So, a User service would use the User model and the Favourite model to manipulate and display user favourites.
Related
I'd like to ask other opinions about code structuring of business logic on Laravel applications, mainly regarding permissions at the row level.
For those that don't know it, Laravel is a MVC framework for PHP, much like Rails.
For the sake of understanding, let's suppose a multi-tenant application where each user has his own albums and pictures, so far so good.
Now, each user can invite others to collaborate (by uploading photos) into his album.
Both, the album's owner and collaborator that uploaded the picture may be able to delete or update information about that picture.
Only the owner may edit the album and invite new collaborators.
Collaborators can remove themselves of the album if they want so.
Pinterest should be a nice example of something similar, but our application is probably 3 or 4 times more complex.
The question is: where should I handle that kind of logic?
Laravel proposes the approach of having repositories, entities and services, which I don't fully understand, probably because of the lack of good examples. So the obvious first choice to meet those deadlines was to put it all on controllers (ew!). Now, digging into refactoring, there are many possible ways to un'spaghettize our code:
I've seen people implement ACL at row level (looks kinda dumb and overkill)
It would be possible to turn models into behavior aware objects and not only data containers, something like $album->add_photo($photo) and check permissions at that function
It would also be possible to override model's save method and do there those checks
Or, follow the Laravel proposed road of having separate layers of concern
I suppose that having methods like $album->can_be_edited_by($user) may simplify the displaying of 404 erros on routes not allowed, hiding view's links as well as validating before saving the models
Which would you recommend, and does anyone know any simple, but understandable, example of repositories, entities and services not using .NET?
Thanks!
Edit: I guess that a full ACL system would cause excessive overhead, since there may be thousands of resources associated with each user, but only one role per kind of association. For instance, pictures will have an uploader_id and albums will have an owner_id.
I could be wrong but I think ACLs are OBJECT based permissions (i.e., a user can or can't delete photos in GENERAL). What you want is more custom MODEL based permissions (row level like you said), i.e., a user can delete photos that they themselves created (SPECIFIC ones).
Most Laravel packages are designed for object based permissions I think, but not https://github.com/deefour/authorizer - this one is a great hidden gem. We don't use it in our project but I found that it really covers all the bases we'd need.
We have really advanced model permissions on our app, I have them scattered throughout my models, but I take a very model centric approach, which isn't necessarily very "laravel-esque". In your example with delete, I would override the delete method in your model or listen for the eloquent event and prevent it there. If you have to prevent read/write on certain attributes you could even do that by extending your validator or using custom mutators/getters, serializers or listening on events. More on where to add business logic in my question/answer here: https://stackoverflow.com/a/27804817/796437
I'm still trying to find the best approach, if I do I'll update this - but thought I'd post.
In Laravel you can use Policies or use solutions, like Symfony Voters.
For Laravel exists same package - Laravel Simple Voters.
Using this, you can check access to custom objects, looks like this:
Access::isGranted('edit', $post) // current user can edit this post?
You can put this logic, to example, into middleware, if you wish check requests to controllers.
I'm using CakePHP 2.2.3 and I need to build an admin/dashboard area for my site.
I have many models and controllers related to this models and in the dashboard I need to have the ability to CRUD all posts/users/news etc.
Obviously I need to build a Dashboard controller with some index action which will be show dashboard "home" page.
My question is: where to put all other actions – for posts/users/other things adding/editing ?
Should I put all this actions in this new dashboard controller or it's better to put this actions to related controllers(Posts/Users..)?
Keep your specific actions in each of their own controllers. A DashbaordsController is fine for whatever pages need to display a lot of different model information, but CRUD actions should be kept in their own Controller.
If you want/need a single page to be able to actually do the CRUD actions ON that page, you can use ajax and STILL call the actions of that specific Controller.
Bottom line, if you try to put all your CRUD into a single Controller, it's just going to get messy, and will be very confusing for future programmers (which includes yourself 6 mo from now).
It's so easy to include data from other Models $this->loadModel('MyModel');, that doing CRUD actions in their own respective Controller is not much of a hindrance. Again - the DashboardsController is still fine for those few pages that really are like dashboards, and have no alliance toward a specific model. But not for each model's CRUD.
Generally the ideal way is to do skinny controllers and keep logic as far down the stack as possible that is near to the models. Ideally you'd want to introduce libraries for code reuse and testing. Robert Martin aka Uncle Bob says that the web delivery and databases should be as much of a plugin as possible. This lets you unit test much better. As far as your particular case i'd want to keep it close to REST as i can so separate controllers ideally delegating to some lower level stuff.
I have 3 tables that contain user information, one for students, one for teachers and one for administrators.
They are not related in any way. I wan't to create a dashboard for the Administrators, where a list of students and teachers shows up.
The only way I found to achieve this was using the $uses variable in the Administrators controller. However, I have read in many places that this is bad practice.
Any solutions?
Another, perhaps better practice is the use of ClassRegistry::init('MyModel')->myMethod() (more reading # Cake API)
This only loads the object when it's used, as opposed to loadModel or uses, with ClassRegistry the models are treated as singletons.
--
that you are doing something wrong: you need access to a model that has nothing to do with your current controller.
There are plenty of conditions where you would need to access all of your models data, from one controller, but never a definitive answer on how to do it without breaking convention!
You can always use another Model which is not related by using
$this->loadModel('NewModelName');
Then you can access new loaded model by:
$this->NewModelName->add(); // whatever method model has defined
Why prefer loadModel() over uses?
To gain performance. How? uses calls the loadModel function itself to load all the models you specify in uses array. But the problem is if only one of your action needs a particular model, whats the good thing to include it in every action. e.g. only add() action requires an unrelated model, but if you have specified it in uses array, no matter what action gets called a completely unrelated model is going to load. To put simply it will be inefficient. Its like you have declared variables in a C programme but never used them. In case of C compiler will warn you that you are not using your variables, but unfortunately cake couldn't tell you.
Its alright to use uses if all your actions needs to load that model, use loadModel() otherwise.
You probably didn't read my answer in your other question :))
I have 3 tables that contain user information, one for students, one for teachers and one for administrators. They are not related in any way. I wan't to create a dashboard for the Administrators, where a list of students and teachers shows up.
The problem is you are separating similar data into 3 different tables, in this case, user information. So when you try to manage this data, you hit a brick wall: because you leave out the relationships when you separate them in 3 tables.
The only way I found to achieve this was using the $uses variable in the Administrators controller.
You got the wrong idea about the controller. Each controller manage the data flow of a particular model (and related models). It doesn't mean that you have to stay in Admin controller to do administrative things. What model you want to manipulate decides what controller you need to be in.
However, I have read in many places that this is bad practice.
Now for the main question: using $uses is a red flag that you are doing something wrong: you need access to a model that has nothing to do with your current controller. Now, there're always exceptions in programming, sometimes we need to have access to that model. That's where loadModel comes in. Because it should be rare. If you need the model a lot, then you'll need to call loadModel a lot, which is cumbersome, which is what $uses is for, but then that means something's wrong with your app design :))
So, you can say using $uses is a sign of bad decision (in DB design or application structure); and so is using loadModel a lot.
Edit: Any solutions?
I gave one solution in your other question. But if you want to have them all in one place, you can have 1 users table with user information. Each User can hasOne Student, Teacher, Administrator and a 'group' field to decide what group the User is. The third solution is using $uses. Its performance impact won't be a problem really. But it will be pretty convoluted when you develop your app further. That's what you need to worry about. For example, I can say that, if you use Auth, you'll need to tweak it a fair bit to get it working with 3 models. If you use the users table, it will be a lot easier.
When building applications, whats the best way to decide what goes where. How do you know what functions to put in what controllers and models. For example, I'm building an application that is based highly on location. Users can post different things, that will in turn be shown to other users within a certain distance. Also, each user will have their own profile page that will show everything posted by that user regardless of location.
So I have models like this
class UserModel extends BaseM{
get_user($uid);
get_all_users();
edit_user($new_data);
delete_user($uid);
add_user($new_user);
get_user_articles($uid);
get_user_reviews($uid);
get_user_foo($uid);
}
class ArticleModel extends BaseM{
get_article($aid);
get_all_articles();
add_article($new_article);
delete_article($aid);
}// similar to ReviewModel, and other models
class LocalModel extends BaseM{
get_local_articles($zip_code, $range);
get_local_reviews($zip_code, $range);
get_local_foo($zip_code, $range);
}// holds all location related functions
As you can see, I lumped everything dealing with a user (needs a userID) in the userModel, everything dealing with location (needs a zip-code) in the localModel, and then everything else has its own model.
I was wondering whats the best way to figure out what goes where, is there like a rule of thumb for this kind of stuff?
Well you're 80% there already. You've got your models broken out and that is a big battle. Next design the app that you want. If you end up with a lots of repetitive "elements" on multiple pages, then each element should be a view. Otherwise each page should be a view. Or some combination of the two.
Once you have you pages defined and you know the data flow of the app, all that remains is the controller.
It may be practical in a small app to have a single controller. Or for really complex apps, you may have multiple controllers - no more than one per "page" though.
Just keep in mind - the Model should be view agnostic (you can retool the UI without impacting the model). The views should be blind to where the data comes from or where it's going - everything gets filtered through the controller.
See my previous answer to a similar question here:
I normally use this approach: try to put it somewhere. if after a while you use it, it feels awkward, then it's not in the right place.
In general every model class should have methods that make sense for itself, and eventually return other models. Refrain from putting too much computational intelligence in your models. If there's something that feels strange in either classes, there's probably a third class in between to be discovered.
Currently, I am working on restructuring an existing code base. I'm new to php frameworks, but I do know in general how MVC works.
Right now, there is one controller file, one model file, and thirty view files.
Should every model correspond to a table?
Should every view correspond to an html page?
What about the controller? How can I break this thousand line monster into more organized code.
Thanks.
Should every model correspond to a table?
No. A model is often constructed from data from multiple sources. Don't think in terms of tying it to your physical database structure even though there will probably end up being lots of similarity.
Should every view correspond to an html page?
Not to sound trite, but every view should correspond to a view. I'm not sure exactly what you mean by a "page".
Perhaps an example would be useful. Imagine a user registration page. The model is User and might contain fields such as:
Title
Given names
Surname
Date of Birth
Username
Address(es)
Email address
Phone number(s)
etc
Now, that data may be in multiple tables. For example: Party, Person, Contact and Address.
There will probably be several views:
About page
Form page (used for new registration and possibly editing details as well as errors);
Success page;
Failure page.
Typically all of this will be handled by a single controller as all the processes are inter-related.
Every model should correspond to a logical data object - which should generally predominantly be stored in one table (often with foreign keys into other tables, since models generally need to reference other models).
Every view should correspond to a logical way of viewing your data (e.g. on stackoverflow, there is hopefully a view for the list of badges pages, a view for the list of tags pages etc).
Every controller should correspond to a logical grouping of views, which should not be too big (where too big is the line where the file is becoming unmanageable - if you've got 30 views, you can hopefully find a logical way to group them into say 3 controllers).
What about the controller? How can I
break this thousand line monster into
more organized code.
Have a look at CakePHP framework and how it solves the problem of large models, controllers, and numerous views. I find it quite elegant. Complex models can have behaviours. Large controllers can be broken into components. And numerous views are grouped with layouts, while having common bits separated into elements. It might sound complicated and scary at first, but once you try to use it, it really falls into place.
Should every model correspond to a
table?
It doesn't have to but it often will depending on the complexity of your business logic.
Since you're refactoring an existing application, think about how the model is used by the other layers. In MVC, the model is at the bottom of the dependency stack.
How will the view access the model? How will the controller modify it? How will the model be populated?
Should every view correspond to an
html page?
Again, it doesn't have to but it often will.
What about the controller? How can I
break this thousand line monster into
more organized code.
A common strategy is using the front controller pattern. The front controller deals with HTTP requests, application initialisation and site-wide logic (just as your thousand line monster is currently doing) - but then it delegates to more specialised controllers.
These specialised controllers could be grouped by the models it uses, site page structure, or anything else that seems logical. They then interact with the model and select a view to display.
Finally, +1 to frameworks as Leonid suggested. Even if you don't end up using one, there are some great implementations of controller patterns out there.
Hope that helps.