I'm trying to optimize my code, and i can't decide on what to use, and whichever is the best practice.
I have a view say view1.php, that is rendered by an action. Now view1 contains a model $model that was passed on to the view by its action; now i'm using the $model again to use it in another different action like below:
view1.php:
$studyDetails = $this->actionStudyDetails($model);
and in the StudyDetails action, i'm going to use the $model,
StudyController.php:
public function actionStudyDetails($model){
//do some processing of the model here and return an object
}
My question is, is it a good idea to pass an entire object that's already been loaded, supposing the model is very large? in terms of optimization , or probably best practice?
or should i just pass the id or primary key say $model->id? and then load the model after; making my action like this:
StudyController.php:
public function actionStudyDetails($id){
$model = $this->loadModel($id);
//do some processing of the model here and return an object
}
Should i pass the entire object unto the action or is it best to just reload the model once inside the action? Thanks, i hope i explained it well
I much prefer loading that single row the database. It's an optimization I wouldn't worry about until it becomes an issue.
You can store the model in your controller to prevent running the same query multiple times:
// Store model to not repeat query.
private $model;
protected function loadModel( $id = null )
{
if($this->model===null)
{
if($id!==null)
$this->model=SomeModel::model()->findByPk($id);
}
return $this->model;
}
It's a trick I learned here.
Related
The other day, while developing my PHP project and implementing the User class, i started to wonder how this class should interact with the MySQL database the project is using.
Let me start with an example: let's say I have a getName() method, inside the User class, that returns the user's real name. What's the more fitting way to implement that method?
I came up with 2 solutions:
I put the DB query inside the getName() and only get what I need like this:
public function getName() {
// MySQL query code here
}
I create a load() method inside the User class that load all the user data inside the class structure and then the getName() is something like this:
private $name;
// Load all user data inside the class' structure
public function load() {
$this->name = // MySQL query here
}
public function getName() {
return $this->name;
}
I thought, not sure if mistakenly or not, that the first way is more efficient because i only get the data I need, while the second way is more OOP but less efficient.
So, here's the question: what is the better way? Why? Are there better ways?
Either way, consider storing/caching the results of that so you do not make a query every time you use getName on that object.
Also, consider not wrrying about all that by using a ORM/DBAL Solution like propel or doctrine.
Also check out Lazy Loading and the Active Record Pattern
Run your query just in time and only run it once (unless you know the value might change), try something like the following:
class User {
protected $data;
function getName()
{
if (!isset($data['name'])) {
// if you can load more than just $this->data['name'] in one query
// you probably should.
$this->data['name'] = // query here
}
return $this->data['name'];
}
}
Aside from the question being kinda broad (as there are countless patterns), the second way you mentioned is better IMO, and to add to it I would also suggest supplying ID as a parameter which you could then use to build a single query to fetch the user by ID and then manually assign all properties (from the fetched row).
In my MVC, I populate forms with data from the model using an array. I am using PDO and fetch associative array this gives an array of fields that I can use to populate the view.
When the user requests a new record, the array is empty and unless I explicitly test the value of every variable before use, the script falls over with a non-existant index.
My approach previously was, for new records, to setup an empty array and pass this to the view. This is fine for simple forms, but when the view needs to use 10-15 variables this is a bit tedious.
What is the preferred method of doing this?
Should my view test every field before use, or should I create the empty array. If so, should the empty array be created in the controller or the model?
As an example, If the user wants to edit an existing record, the controller passes the ID number to the model and asks it for an array of current values for the record.
The controller passes this array to the view which then incorporates the values into a form for editing.
In contrast, when the controller receives a request for a new record, it knows there is no point in asking the model for the record because its new. It can't just call the view because the passed array does not contain the keys required by the form.
My current approach to this is to initialise an array of empty keys but this seems tedious, time wasting and prone to error as it needs to be maintained if the model changes.
eg:
$this->view->class['Code']=NULL;
$this->view->class['Description']=NULL;
$this->view->class['Image']=NULL;
$this->view->class['Judge']=NULL;
$this->view->class['Entries']=NULL;
$this->view->class['Absentees']=NULL;
etc
There has to be a better way?
This is kind of a band-aid for poor MVC design, but you could use a function like this,
function idx(&$array, $index = 0, $default = null) {
return isset($array[$index]) ? $array[$index] : $default
}
In the view if you normal did $class['Description']
you would now use idx($class, 'Description')
You should not have to pass each attribute of your model individually. Instead, just pass the entire model object and access it from the view. Example:
$this->view->entry = $myModelInstance; where $myModelInstance is an instance of Entry or whatever class has the Code, Description, Image, Judges, etc attributes.
Then in the view, use something like $entry->Description
In my opinion setting propper defaults is a model issue.
Imagine a class like this:
class Car {
protected $color;
protected $vendor;
public function __construct($color, $vendor) {
$this->color = $color;
$this->vendor = $vendor;
}
public static function getDefault() {
return new Car('unknown', 'unknown');
}
}
However. You told that you use arrays. You'll find a similar way for arrays. I would suggest to write a function like getDefaultWhatever() and place it in model part of code.
I'm using the MVC pattern in my application.
Now I need the view object in a model.
I don't want to add the view as a parameter for my function in the model (since I need it in other functions as well). And I don't want to keep on passing it.
Should a add the view as an attribute for the constructor of the model?
Is there another way? Shouldn't I be needing the view object in the model in the first place?
What would be the preferred way of doing it?
Example:
Controller
function someAction()
{
$somemodel->add();
}
Model
class SomeModel()
{
function add()
{
if ($view->user) {
// do stuff
$this->mail();
} else {
// do other stuff
}
}
function mail()
{
Mailer::send($view->user->email, $this->getitems(), $view->layout);
}
function getitems()
{
return Items::getitems($view->user);
}
}
If you're really doing MVC, then you won't need the view in the model, because only the controller should have access to the view.
Looking at the code you've provided, I can tell one thing: the add() method should not reference $view in any way (even for accessing its properties). Instead, the model should be provided with the $view->user value from the controller. The same goes for the mail() method.
Consider fixing those issues. Otherwise, you'll get into something worse later on.
The model should be separate from the view. So, as mkArtak said, the controller should be the only thing that communicates with the view. Which then passes only the necessary information to the model.
As for the model, it should really only deal with the information that it understands.
i.e. if you had a Car model... you don't want to build it dependent on it's factory. If you did, you would have to change your code if you wanted to build it in different factory.
The controller is where you 'bake' everything prepare for render. By bake I mean you consider any passed in $_REQUEST params, make model API calls to get the data you need, and set template variables to be rendered. Your action, at the end of this process should make a call to a template (view) you choose in order to render the 'baked' template variables.
If I want to gather some data from the database and use it in multiple actions of a controller, how would I do this?
Currently I just access the model and retrieve the data separately in each action (yes, I know this is inefficient... it's why I'm asking you all!):
public function indexAction()
{
$model = $this->_getStuffModel();
$this->view->results = $model->fetchEntries();
}
public function anotherAction()
{
$model = $this->_getStuffModel();
$foo = $model->fetchEntries();
foreach($foo as $k => $v)
{
//manipulate the data
}
$this->_helper->json($theoutput);
}
If I were not doing this in Zend Framework I would just put it in the constructor and then be good to go with $this->whatever in any method of the object but I don't see how to do this in ZF (I tried sticking it in init() but it didn't work... maybe I just made a stupid coding error and that is actually the correct place to do this?).
*EDIT: If I have, say, 7 actions in a controller and I put the code that hits the database in the init() method then I will hit the database with each and every action that is called. That won't do.
The init() Action is called on every request against the controller. If you do:
protected $model;
public function init()
{
$this->model = $this->_getstuff();
}
public function indexAction()
{
echo $this->model->name;
}
than it should work.
I think the #ArneRie init() solution is what you want.
Another solution will be to cache the Zend_Db_Rowset object with all the entries using Zend_Cache and serve it from cache. You can hit the database only when new data is added ( and the save function can register an observer that will delete related cache entries), or maybe each 10 minutes ( and you check the timestamp of the cache entry) or another business logic of your choosing.
I have a controller which has several methods which should all share common informations. Let's say my URI format is like this:
http://server/users/id/admin/index
http://server/users/id/admin/new
http://server/users/id/admin/list
http://server/users/id/admin/delete
I need to retrieve some informations from the database for id and have them available for all methods instead of writing a line in each of them to call the model. How can I do this?
class users extends Controller {
private $mydata = array();
function users()
{
parent::Controller();
....
$this->mydata = $this->model->get_stuff($this->uri->segment(2));
}
function index()
{
$this->mydata; //hello data!
}
Here I simply hardcoded the array (which probably is a really bad idea). Nevertheless you can store the data in a codeigniter session if you need to. Codeigniter can store this data in a cookie (if it's total is less than 4kb) otherwise you can store bigger blobs of data in the database (see the docs on how to do this).
See: http://codeigniter.com/user_guide/libraries/sessions.html
Subsection: Saving Session Data to a Database
Here's some session exercise:
$this->session->set_userdata('mydata', $mydata);
....
$mydata = $this->session->userdata('mydata');
If this cannot be solved from CodeIgniters Hook mechanism, you could override the constructor method in your controller and call your own. Judging from their SVN repository you'd probably would do something like
class YourController extends Controller
{
function YourController()
{
parent::Controller();
$this->_preDispatch();
}
function _preDispatch()
{
// any code you want to run before the controller action is called
}
Might be that the call to preDispatch has to be before the call to parent. Just try it and see if it works. I didnt know they still use PHP4 syntax. Ugh :(
Based on your url structure, and the fact that codeignitor uses a MVC pattern, I'm assuming you're using mod_rewrite to format the url path into a query string for index.php. If this is the case, the value of "id" should be available at $_REQUEST['id'] at any point in the execution of the script...