views:

156

answers:

4

Here is a quick overview of the controllers functionality in most of the application:

  • controller loads a specific model, gets data from it, formats the data and passes the formatted data to the view.

Now there is a search page, which needs to do a search query over entire database (all models). It needs to show each type of data in its particular formatted output on a single page as a list.

The problem:

The search controller can do the search, dynamically load model for each record type, and get the data from model. Problem comes when the data needs to be formatted. I am trying to load the specific controller from the search controller, which is causing problems.

What to do?

PS: I tried using the 'Wick' library, but it fails when the controller's format function tries to use its own model and session object, giving errors about call to a member on a non-object.

A: 

It sounds like you want to use the Factory design pattern

Make this a library:

class MyModelFactory {
  static public function Factory($data) {
    $type = key($data);
    return new $type($data);
  }
}

now, in your controller, you can do something like this:

$model = MyModelFactory::Factory(array($_REQUEST['model'] => $_REQUEST));

and now you have an object of whatever model was specified in $_REQUEST['model']. Be sure to take any security precautions you may need for your application to assure the user has permissions to use the model that they request

Now, since you want to be using common methods and stuff, your models should probably be based off an abstract class / interface.. so instead of

class MyModelOne extends Model {
  // stuff
}

You probably want something like this, to ensure your required methods will always be available:

abstract class MyAbstractModel extends Model {

  protected $search_params;

  public function __construct($data = array()) {
     $search_params = $data['search_params'];
  }

  protected function GetSearchParameters() {
    return $this->search_params;
  }
  abstract public function GetData();
  abstract public function GetColumns();
  abstract public function DefineViewOptions();
}

class MyModelOne extends MyAbstractModel {


  public function GetData() {
    $params = array();
    $params[] = $this->db->escape_str($this->GetSearchParameters());
    // return whatever data you want, given the search parameter(s)
  }
  public function GetColumns() {
    // return some columns
  }
  public function DefineViewOptions() {
    // return some configuration options
  }
}
Stephen J. Fuhry
As I see it, the model is directly affecting the output of the view by returning view options. That breaks the MVC pattern right?
Samnan
A: 

In general you can't load another controller from within a controller in CodeIgniter (although there are mods that allow you to do something like this).

I would try creating a class for formatting your data and add it to the application/library folder. Then load, use and re-use this class throughout your various controllers.

Here is a page from the CodeIgniter documentation Creating Your Own Libraries that explains the details and conventions.

Also, if a class is overkill, creating helper functions is an even lighter approach.

The difference between libraries and helpers in CodeIgniter is that libraries are classes, helpers are just a group of php functions.

Once you have formatted your data, you can load any view from any controller, so you should still have all the re-usability you need so you DRY (don't repeat yourself)

JohnWright
What if, the function I am trying to call from the second controller uses its properties and model to do its work. Do I move/copy all that functionality to the helper/library ???
Samnan
If that's what it takes, yes. It may be that you are trying to do too much in your controller class. I don't know all the details of your code, but it sounds like you have these elements in your controller..1. Retrieve your data (model)2. Format your data3. Pass the data to the view for display..The controller should just tie these actions together. 1 and 3 are provided by the framework, but in 2 you could take everything out of the controller involved with formatting, and make a reusable library for it. Again, I don't know the details of your code, but this is how I would approach it.
JohnWright
A: 

There are a few simple approaches based on the principle of what's simpler (versus what's perfectly DRY). Here's one alternative approach I use with CodeIgniter:

  1. Instead of trying to load multiple controllers, reuse the view fragments from your search controller (or search route, depending which you're using). This requires using the same naming conventions for your data elements so the views are interchangeable, but you should be doing this anyway.
  2. Instead of using multiple models for search, add a single Search model that knows about the things that can be searched on. If you want to prevent duplicate SQL, reuse the SQL between models (this can be done using constants, or loading SQL from disk).

Controllers are not great candidates for reuse from your own PHP code: they route actions and requests for resources to the things themselves. They are intended to be called via HTTP, using the URI interface you've come up with. Calling them from code is a coupling you want to avoid. That said, reusing controllers from JavaScript (or via cURL) is a great, decoupled way to reuse things in any web framework.

Bruce Alderson
hi Bruce, the view is common among the controllers. It's the formatting function inside controller which is causing the issue. Read my question again if it is not clear to you. As for the search model you suggested, it will mean a model which knows about each and every tables structure and fields for the entire database, I do not consider that really useful.
Samnan
+1  A: 

After much refactoring and trial/error, It appears that the best way to achieve the above is this way:

  1. Keep the format function in the base controller from which all other controllers are derived. The format options are passed to the function along with the data object as arguments.

  2. Make a static function in each derived controller, which returns the formatting options of the data.

  3. Inside the search controller (which is itself derived from the base controller), for each data object, call the static function of its particular controller which returns the data formatting options, then use that to format the object for the view.

I guess I can say I will stick to using the model only for interaction with the database, and let everything else be done by controller. If anyone has a better solution still, I am all ears.

Samnan