I am new to MVC pattern. I googled, wrote code snipped, played with lot of code. But still confused about the ultimate relationship between controller and model.
Before MVC my programming style was something like this.
class Users extend Database{
function __construct(){}
public $id,$name;
public function Save(){
$this->Execute("[Built query using the two member variables]");
}
}
And I used to use this class in my HTML as
$user = new User();
$user->id= "u1";
$user->name = "sarah";
$user->Save();
So, How can I bind my old understanding with yii model - controller thing?
My Exact Confusion:
1)When I create model for a table from the command i didn't find any property definition in the model for each column of the table. Instead in the controller this line is found $model->attributes = $_POST['Message'] what the hell is this line?
Isn't it better this way:
$model->message = "hi";
$model->date ="10-10-2011";
$model->save();
well to understand the single line
$model->attributes = $_POST['Message'];
we have to look into the model class. in model class (extends CActiveRecord, usually auto generated by gii) we have two functions of importance, attributeLabels and rules.
The attributeLabels lists all the models properties (or variables, or the columns we want to store in database, or simply attributes).
In rules function we have all the rules set for each and every variable/column/attribute.
In autogenerated form, these rules directly reflect our database structure, and in some cases we don't have a rule so just written the line
array('name', 'safe'),
This rule indicates that no rule is applied and it's safa to save the variable in database.
Now when in controller (or anywhere) when we have a $_POST['Message'] and we apply the single line
$model->attributes = $_POST['Message'];
All our posted values are applied to $model, i.e. we don't to go through each and every attribute/properties' validation and assignment and after just the single line
$model->save();
everything is get saved to database after validation. That's the beauty of using Model (CActiveRecord in this case).
$user = new User();
$user->id= "u1";
$user->name = "sarah";
$user->Save();
Now that code you should write in controller action, and this
<span><?php echo $user->name ?></span>
Is your view.
Thats what Active Record is for, what you have there, is the model's logic, there is no logic there that you should put in the controller, read about the active record pattern
Related
Just a quick question. I see the following code in an extension and I am not sure what it is doing.
public function actionCreate() {
$model = new User('register'); <----(this is the line I am confused about)
//other stuff...
}
What is the "('register')" doing there? Is it an argument going into the
"User" class? I've looked in the user model, useridentidy, and webuser, and cwebuser classes, but can't find anything. I know that without the proper context, this might be difficult to explain, but in general, what is this extra stuff after the declaration of a new object in Yii? I've been creating objects to use as active records by just typing this:
$model = new User;
(using the "User" class again just as an example)
I'd appreciate any help to clarify this issue.
It's a scenario. Models in Yii can have multiple "scenarios" affecting how validation is performed and which attributes can be assigned in bulk. In this case an object of User class is instantiated with the register scenario, which defines a registration-specific set of validation rules.
$model = new User('register');
This line is for binding the object i.e $model in this case to a scenario which is 'register'.
It is similar to
$model=new User;
$model->scenario='register';
You can set the scenario in this way too. But in order to avoid multiple lines or for the ease of the developers it can be done in this way too :)
Also you can call different scenario in one model:
In your Model rules function:
public function rules(){
return array(
array('username,email', 'required','on'=>'register,update'),
array('firstname,lastname', 'required','on'=>'other scenario here'),
);
}
And where you want to call your custom scenario like in Controller action!
$model = new User('update');
Or
$model = new User('register');
It's an argument being passed to the constructor. Look in the User class for a function called __construct, it will probably accept an argument, and you can see what it is doing.
This is not unique to Yii, any class can accept arguments in it's constructor.
The way I create CodeIgniter models at the moment is (i.e. no constructor, having to pass userID all the time and limited to one object):
$this->load->model('User');
$this->user->set_password($userID, $password);
But I would like to do it like this:
$this->load->model('User');
$User = new User($userID);
$User->set_password($password);
UPDATE: Perhaps just a user model was a poor example.
For instance, if I have a shopping list that has various items I would like to use PHP in this way:
$this->load->model('List');
$this->load->model('Item');
$List = new List();
$items[] = new Item($itemName1, $itemPrice1);
$items[] = new Item($itemName2, $itemPrice2);
$List->add_items($items);
CodeIgniter feels fundamentally broken in handling PHP OO in this way. Does anyone have any solutions that can still use the superobject within every model?
You can do it like you normally would:
require_once(APPPATH.'models/User.php');
$User = new User($userID);
Or, you can rely on a table-level model to return a record-level model:
$this->load->model('users_model'); // users (plural) is the table-level model
$User = $this->users_model->get_user($userID);
Meanwhile, in users_model
require_once(APPPATH.'models/User.php');
public function get_user($userID)
{
// get a record from the db
// map record to model
// return model
}
you can subclass the model or just add something in the constructor
I think you need to adjust code to do it the way you're looking to…
I currently have code in my controller which retrieves 3 types of entities from a db, in order to populate 3 select field. The controller also takes responsibility for invoking the form objects validate method, and upon success, it instantiates and sets an entity's properties from the form inputs and calls on a data mapper to persist it. I have a feeling that my controller is way too fat. Any suggestions on where I should put my form preparation and processing code? Should I move it into the form object? Thx in advance for your time!
I prefer to put form processing in the form itself. So I create a process() method in the form so then you can keep all of the form logic encapsulated in the form object.
For example in your controller:
$form = myForumClass();
...
$form.process();
You might want to take a look at Using Zend_Form in Your Models by Matthew Weier O'Phinney. It explains how to move this kind of logic from your controller into the model.
For your database tables you should create models - these should contain the logic to get and set data into that table. So in this instance the model should be responsible for getting an array of values which is then passed to the controller and then to the form.
class MyModel
{
public function getSelectArray()
{
//Get stuff from Db here and format into an array ready to add to the form
}
}
You can then do this in your controller
$model = new MyModel();
$element->addMultiOptions($model->getSelectArray());
You could also create a specific form class which extends Zend_Form, this could then be called from any controller and return the same form - allowing you to reuse the form anywhere easily - e.g. you may want the same form on a registration page as on the My Profile page.
As an example, below is a function from a model of mine to do just this:
public function getSelectArray()
{
$select = $this->select()->order(array("name ASC"));
$categories = $select->query()->fetchAll();
$return = array();
foreach($categories as $category)
$return[$category['id']] = $category['name'];
return $return;
}
Extended Question: Why should I use data mapper / Db_Table_Row, where as DbTable is capable of handling most of the basic tasks for data manipulation.
I am currently learning ZF v1.11
For Database manipulation, I created DbTable for each tables. For example, "users" table is represented by Application_Model_DbTable_Users with no additional codes in there.
When manipulating data, I can use:
<?php
$uTable = new Application_Model_DbTable_Users();
$newUid = $uTable->insert(array('name'=>'Old Name', 'email'=>''));
$user = $uTable->find($newUid)->current();
// Then I can use $user which is instance of Table_Row
$user->name = "New Name";
$user->email = "email#addr.com";
$user->save();
My Question is, when would I need to define a row class (assuming Table_Row is referred as DataMapper in ZF-Tutorials)
// By, adding this to the DbTable class
protected $_rowClass = 'Application_Model_User';
What are the benefits of having a Row class for each entity? Can anyone point me to best practices for this.
You do not need to define your own Table_Row. However, it maybe useful in many cases, particularly if you want to define some specific methods or properties for a given user row. They can also improve readability of your code.
For example in your Users table case, you could define a method called getFullName() in a custom user row as:
public function getFullName() {
return $this->firstName . ' ' . $this->lastName;
}
Then when you obtain user row object, to get the full name of the user, you just do:
$user = $uTable->find($newUid)->current();
$fullName = $user->getFullName();
Second example is when you have some parent table to the Users table, such as Addresses. In this case you could define a method called getAddress in a user row:
public function getAddress() {
return $this->findParentRow('Application_Model_DbTable_Addresses');
}
In this scenario, you would get an Address row object for a current user as follows:
$user = $uTable->find($newUid)->current();
$addressRow = $user->getAddress();
Another example, would be when you want to create custom delete or instert methods. Lets assume that you want to make sure you do not want to delete an admin user using delete() method. Then you could overload delete method from Zend_Db_Table_Row as follows:
public function delete() {
if ('admin' === $this->userRole) {
return 0;
}
return parent::delete();
}
This way, you would not be able to delete an admin user just by calling delete() on a user row object:
$user = $uTable->find($newUid)->current();
$rowsDeleted = $user->delete(); // would be 0 if $user is admin
These are just three basic examples showing usefulness of defining your own row classes. But of course they are not necessary. However, from my own experience they are quite handy.
In a nutshell: it's about isolation.
Zend_Db_Table is an implementation of the Table Data Gateway. It channels CRUD access to a specific table view through one class. It is usually used with a Table Module, e.g. a class that contains the business logic for the records handled by a gateway.
Zend_Db_Table_Row is an implementation of the Row Data Gateway Pattern. Here, the returned objects look exactly like a database record and they contain the business logic to work with that data, but they don't contain the logic to CRUD with the table they are from (that would be an ActiveRecord) but aggregate them.
Row Data Gateways are fine as long as you don't have too much object relational impedance mismatch. How an object is persisted in a relational database and how it should look like in an object world are often quite different things. When using a Domain Model, your business objects are usually structured in a different way than they are stored in the database. Thus, you cannot easily CRUD them from the database. This is where DataMapper comes into play.
A DataMapper takes the responsibility of mapping Domain objects onto Recordsets and vice versa. This makes your application more maintainable because it decouples your Domain objects from the Database structure. It keeps them separated and gives you more flexibility in how to model both layers (persistence and domain).
I currently work on a small application with models, mappers and controllers.
My question is, (because I did not found any matching answer), how does the mapper interact with the model (& the controller), when we have the following situation.
$user = new UserModel();
$user->setId('21');
$userMapper = new UserMapper($user);
$userMapper->retrieve();
This will work as fine as possible, the model has an id with which the mapper can retrieve the needed user (and map it back into an user object).
My problem is, how can I wrap this code, I mean, this code is very raw and it is definitly not very recommended to be used in a controller.
I want to shorten it, but I do not exactly know how:
public function view($id)
{
$user->find($id); // this seems always to be tied with the user object/model (e.g. cakephp), but I think the ->find operation is done by the mapper and has absolutly nothing to do with the model
$view->assign('user',$user);
}
It should look more like:
public function view($id)
{
$mapper = $registry->getMapper('user');
$user = $mapper->find($id);
// or a custom UserMapper method:
# $user = $mapper->findById($id);
$view->assign('user',$user);
}
But this is much more code.
Should I include the getMapper procedure within the parent controller class, so I can easily access $this->_mapper without explicitly calling it?
The problem is, I do not want to break the mapper pattern, so the the Model should not access any SQL/Mapper method directly by $model->find(), but I do not want to have a lot of code just for first creating a mapper and do this and this etc.
I hope you'll understand me a little bit, myself is already confused enough, because I am new to many patterns and mapping/modelling techniques.
You could add a Service Layer, e.g.
class UserService
{
public function findUserById($id)
{
// copied and adjusted from question text
$user = new UserModel();
$user->setId($id);
$userMapper = new UserMapper($mapper);
return $userMapper->retrieve();
}
}
Your controller won't access the UserModel and the UserMapper directly, but through the Service.