I am trying to follow good practices as much as possible while I'm learning using OOP in an MVC structure, so i'm turning to you guys for a bit of advice on something which is bothering me a little here.
I am writing a site where I will have a number of different forms for members to fill in (mainly data about themselves), so i've decided to set up a Member controller where all of the forms relating to the member are represented as individual methods. This includes login/logout methods, as well as editing profile data etc. In addition to these methods, i also have a method to generate the member's control panel widget, which is a constant on every page on the site while the member is logged in. The only thing is, all of the other methods in this controller all have the same dependencies and form templates, so it would be great to generate all this in the constructor, but as the control_panel method does not have the same dependencies etc, I cannot use the constructor for this purpose, and instead I have to redeclare the dependencies and same template snippets in each method. This obviously isn't ideal and doesn't follow DRY principle, but I'm wondering what I should do with the control_panel method, as it is related to the member and that's why I put it in that controller in the first place.
Am I just over-complicating things here and does it make sense to just move the control_panel method into a simple helper class?
Here are the basic methods of the controller:
class Member_Controller extends Website_Controller {
public function __construct()
{
parent::__construct();
if (request::is_ajax())
{
$this->auto_render = FALSE; // disable auto render
}
}
public static function control_panel()
{
//load control panel view
$panel = new View('user/control_panel');
return $panel;
}
public function login()
{
}
public function register()
{
}
public function profile()
{
}
public function household()
{
}
public function edit_profile()
{
}
public function logout()
{
}
}
What I would do, is to avoid shoving everything in one controller, and instead separate functionality accordingly - for example, you could have a Registration_Controller to deal only with members' registration, Authentication_Controller, Profile_Controller, and so on, this way is easier to visualize what each part of your application is responsible for, instead of having one single controller with lots of responsibilities, which leads to confusion and other maintainability issues, at least IMHO, it has worked for me.
Going back to your concrete question about the control panel, yes it makes more sense to take it out of the controller, specially if is not an action of it, and as you mentioned you can have a helper class for all the repeatable display logic. If something doesn't make sense within the context of the controller, take it out.
There are some cool frameworks such as Cake and Zend FW, that make life easier at designing/developing MVC application, and that come with a rich set of components that work out-of-the-box.
MVC and Fw's are not mandatory though, it all comes to the scope and your special needs, some times they are of great deal help, most of the times I use them, but sometimes overcomplicate things. Keep it simple ;)
Cheers,
M.
Related
I've a quick question related to the software architecure. In my application I have a model which contains a method to check the environment the application works in. Let's say the model is called "AppModel".
So, the AppModel::isDevEnv() indicates whether the app is runnig in production or development. It's easy to call this method inside others models, components etc.
The problem is when I want to check the environement inside a view. I created a helper with a propriety method inside just to cover the method from the model and return the result coming from exactly model's method.
class AppModel {
public function isDevEnv() {
return boolean;
}
}
class AppHelper {
public static function isDevEnv() {
$app = new AppModel();
return $app->isDevEnv();
}
}
Is it correct approach? Maybe it's a little bit overcomplicated? Maybe I should just make a static method inside a model and call it whenever I would like to call it?
If this is a legacy system I would recommend to refactor it to the desirable solution. If you want to have this helper or it is a required step for further refactoring then do it.
In general I would inject services which behave differently based on the environment instead of checking the environment inside your models. But it might not be easy with legacy system.
i've some questions on controllers structure for limiting duplicating code.
for example i want to retrieve men and woman. What's the best method to do this:
class User {
public function men() {
//render
}
public function women() {
//render
}
//OR
public function by_type($type) {
//render
}
}
It's a simple example but the number of type can grow. And each type can have seperate views. I'm searching for a scaling solution for the future. A best practice for this case of use.
Thanks
Rails has a (somewhat controversial) principle called fat model, skinny controller, which basically means that you can use the controller to process logic for the views, and let the models handle the "heavy lifting" so-to-speak
CakePHP / Rails
To port from CakePHP to Rails, I would highly recommend looking at using the models as much as possible, as it allows you to create an application which utilizes the full performance structure of the server, and not just leave all the logic in the controllers, as is what many people do with CakePHP
Specifically for your issue:
#app/controllers/users_controller.rb
def index
#user = User.gender(params[:gender])
end
#app/models/user.rb
def self.gender(type)
where?("type = ?", type)
end
This allows you to keep your controller as thin as possible, thus allowing for the correct distribution of code throughout the application
I see Rails as a lot different than CakePHP, in that it helps you create really functional & content-rich applications that utilize the entire server, rather than just providing a layer to make a website dynamic
As far as I understand your question, you could call the function that really render the type inside the by_type function this way:
public function by_type($type) {
if (method_exist($this, $type) {
return call_user_func(array($this, $type));
}
else {
throw new Exception('method do not exists!');
}
}
This way you only need to write the method that render the type and call it using by_type method.
I'm working on a PHP MVC but struggling to handle certain aspects of the design, making the User information accessible easily to everything is really puzzling me.
I did have User as an abstract class so you could just use User::getUserId() but I'm not sure that was the right way to do it?
I've now changed it to the following but it's not working.
The Problem
Say I have a basic controller where I want to instantiate the User class every time it's run:
Controller
class controller{
public $user;
function __construct(){
$this->user = new User();
$user->sessionRefresh();
$user->getUserId();
}
}
and then I have a User model class that was instantiated:
User Model Class
class User{
var $userId;
function sessionRefresh(){
if (isset($_SESSION['userId']) && $_SESSION['created'] + 15 * 60 < time())
session_regenerate_id();
}
function getUserId(){
$this->userId = $_SESSION['userId'];
}
}
and then I have more controllers that extend the default controller:
Forum Controller - Very basic just to demo what I'm trying
class forumcontroller extends controller{
function __construct(){
require 'templates/forum.php';
}
}
Forum Template - Very basic example
<html>
<title>Forum</title>
<body>
<div>Welcome User: <?=$user->userId;?></div>
</body>
</html>
Is what I'm doing completely wrong?
I've only recently tried to take the leap from procedural to mvc/oop in PHP and clearly it's confusing me still.
Use $this->user->userId; instead of $user->userId;
You have to echo .... <? echo $user->userId; ?>
Stop editing your code while someone is helping
Thx for Downvote
Yes, what you are doing is completely wrong, when it comes to MVC design pattern. Partially it is related to the fact, that MVC is an advanced pattern. You should not even be trying to use it until you have solid understanding of OOP.
So .. the problems with your code.
When you use include or require, the file which you "embedded" is ins the scope in which it was used. Read about variable scope and template
Model is not a class or an object. It is a layer, which contains multiple structures of different types of structures. This should cover the short version.
Interaction with User instance would be part of domain business logic, which is responsibility of model layer. None of this should be in the presentation layer (which is mostly composed of views, controllers and templates).
View is not a template. In MVC design pattern views are structures which are responsible for most of user interface logic. The decide what sort of response should be returned (wither a HTML composed from multiple templates, some XML or JSON file, maybe just a HTTP header).
The var statement was used in PHP4 to define class variables. In PHP5 we have public, private and protected.
In general, you should not have public variables in class. The break the encapsulation.
Class constructors should not perform computation. It makes the code hard to test.
Update
The bottom line is this: **stay away from MVC* for at least 3 more month. You need to learn a lot more about OOP before you can fully utilize and understand MVC design pattern.
I would recommend the list of materials at the bottom of this post. Start with the first two books, then watch the lectures and then the PoEAA (last one in the list) book.
I am trying to put this function ((click_add)) in library so that i can call it from all the controllers. I already have get_ads() function in library. I tried various ways to shift the click_add(id) function to library and call it to view along with get_ads but doesn't work. Please help
function __construct() {
parent::__construct();
$this->load->library('ads');
$this->load->model('MGlobal');
}
public function index(){
$data['banner']= $this->ads->get_ads();
$this->load->view('test',$data);
}
//i want this in library but no luck
public function click_add($ads_id){
$ads_site = $this->MGlobal->getAds($ads_id);
$this->MGlobal->add_ads_view();
redirect($ads_site['url']);
}
//and views is like this
foreach($banner as $k=>$list){
echo anchor('test/click_add/'.$list['bannerid'],'<img src="'. $list['image']. '"/>');
}
please suggest me how do i achieve that with library
It's also important to remember the role of each part of the MVC pattern. In your click_add() method, it looks like you're rendering a view and causing a redirect. These are two things best suited for a controller rather than a library. Rendering views and redirecting are two things that must be a controller's responsibility, and indeed, you won't be able to access them through the URL, which is what you're trying to do here.
If you want to reuse this method across multiple controllers in your site, try creating a MY_Controller core class and extending your controllers from that. That way, any methods you define in the MY_Controller will be available in any controller you subclass from.
Without any specific error messages or a more verbose description of the problem you're having, I'm afraid there's little more help I can give you.
For projects written in php, can I call more than one (or multiple) controller in class controller? Example in http://img192.imageshack.us/img192/7538/mvc03.gif
ASK: I need to call an action from another controller... And if I do like the picture above, I'm being out-ethics?
Thanks,
Vinicius.
I'm sure that you can do what you want with whichever framework you're using. If you can't do it natively for whatever reason, then you can extend your framework as required.
Having said that, I personally don't like the idea of a controller calling another controller. It seems to somewhat break the MVC paradigm if only from a theoretical standpoint. What I might do instead is build a library class that contains the functionality required and then have both controllers instantiate that class as a member and call the functions required.
For example, using CodeIgniter:
libraries/MyLib.php:
class MyLib
{
public function MyFunc()
{ /* do whatever */ }
}
controllers/ControllerA.php:
class ControllerA extends Controller
{
public function index()
{
$this->load->library('MyLib');
$this->mylib->MyFunc();
}
}
controllers/ControllerB:
class ControllerB extends Controller
{
public function index()
{
$this->load->library('MyLib');
$this->mylib->MyFunc();
}
}
out-ethics? Anywhose... back to reality.
Yes, a controller can call another controller's action. For instance, in cakePHP, this functionality is afforded via requestAction
// pass uri to request action and receive vars back
$ot3 = $this->requestAction('/stories/xenu');
If you're rolling your own, the details of how to implement it will be very specific to your framework.
then you need to modify framework, find place where controller is lounched and add there your second controller.
what framework you are using?
You can do it any way that you want. You don't have to use MVC if you don't want to. However, in MVC you really should only have one controller active at a time. You probably want multiple Views or Models, not another Controller. There is nothing at all wrong in loading, say, a header and footer view for the menu and footer of the site.
If you are building another Controller, then feel that you need to access the functionality of a previous Controller to access its functionality (because it works with a specific / desired Model), then the Model you developed for the latter probably needs to be refactored. IN plain speak, your target Model may be doing too much. Break it up.
You are trying to avoid repeating yourself (DRY) by using calling the methods of a Controller that has already been developed, but in doing so your are creating TIGHT coupling between both controllers! If something changes in the borrowed controller, it will have an effect on the borrowing controller. Not good, Dr. Jones.