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.
Related
I'm trying to figure out if this is a logic issue on my part or just a lack of knowledge.
I have a static method ConnectionFactory::getConnectionInterface($config['type']), which returns a string, a class name. That class has a method on it, createConnection. I'm trying to figure out if I can do it all in one line or not. I tried various things like
new {ConnectionFactory::getConnectionInterface($config['type'])}()->createConnection();
Switching the {} for (), adding them around the whole new, etc. I feel like I get closer in some parts, further in others.
I know I could just have the factory return a new instance of the object (and from my understanding, that may be the right way to do it?), but I'm hoping to figure out how I can write this code, or if I can't.
You need to wrap the new object in ()
(new ConnectionFactory::getConnectionInterface($config['type'])())->createConnection();
Alternatively you could return an instance of the class instead of the class name.
public static function getConnectionInterface($type) {
// generate class name $class
return new $class()
}
Then use just use that object instead of creating a new instance when you call it.
$connection = ConnectionFactory::getConnectionInterface($config['type'])->createConnection()
What's the easy way of calling model method in Yii2. Something like:
$a = User::model()->method();
Code like this:
$a = new User()->method();
don't work.
This is the very basic thing.
Calling model method in both Yii1 and Yii2 is similar and done like that:
$model = new User();
$model->method();
Note that for Yii2 you also need to specify namespace of User class.
The method must be public obviously.
If you don't want use variable assignment, you need to place brackets differently:
(new User)->method();
and not:
new User()->method();
This is PHP language feature, it has nothing to do with Yii framework.
And as for your particular case - model() in Yii1 is used for constructing queries with ActiveRecord.
Replacement for Yii2 is find() method, you can read about it in this question.
Since Yii 2.0.13 you can use instance() to get static instance of model. It works in similar way as model() in Yii 1.1 - creates model object only once and reuses it for every call. It should be faster and more readable than (new User())->method(), which will create separate model on every call.
User::instance()->method();
User::instance()->getAttributeLabel('some_attribute');
Here you can call a method as follows,
$a = new User();
$b = $a->method();
Calling Model Method in any Where the application:
\app\model\ModelName::methodName();
\app\modules\ModuleName\models\ModelName::methodName();
In yii2 you can call a method inside in a model as follows,
$a = Model::method();
I am having some trouble applying Factory Pattern.
I have a class that I usually call as Product($modelNumber, $wheelCount). But in a part of legacy code that I am refactoring, I do not have $modelNumber, and only have $productID, where the link between {$modelNumber, $productID} is in the database (or in my case I can hardcode it, as I only have a select few products at the moment).
I need to be able to create my class using $productId, but how?
Using Procedural ways I would have a function that does the lookup, and I would put that function in a file, and include that file anywhere where I need to do the lookup. Thus do this:
$modelNumber = modelLookup($productId)
Product($modelNumber, $wheelCount);
But how do I do it using Object Oriented way?
Note: I have posted a more detailed situation here: https://softwareengineering.stackexchange.com/q/233518/119333 and this is where Factory pattern (and other patterns, like interfaces and function pointer passing) were suggested conceptually, but I hit a wall when trying to implement them in PHP. It kind of seems like a simple question, but I think there are several ways to do it and I am a bit lost as to how. And so I need some help.
I provided a conceptual answer to your SRP problem on Programmers Exchange but I think I can demonstrate it here.
What you basically want is some other object that will do the work to get you the model number of given product ID.
class ProductModelNumberProvider {
public function findByProductId($productId) {
// The lookup logic...
}
}
Your factory should provide a setter constructor so it can make use of this object internally to lookup the model number if needed. So basically you will end up with a ProductFactory similar to this.
class ProductFactory {
private $productModelNumberProvider;
public function __construct(ProductModelNumberProvider $productModelNumberProvider) {
$this->productModelNumberProvider = $productModelNumberProvider;
}
public function getProductByIdAndWheels($productId, $wheels) {
$modelNumber = $this->productModelNumberProvider($productId);
return $this->getProductByModelNumberAndWheels($modelNumber, $wheels);
}
public function getProductByModelNumberAndWheels($modelNumber, $wheels) {
// Do your magic here...
return $product;
}
}
EDIT
On second thought the setter is not the best approach since having a ProductModelNumberProvider instance is mandatory. That is why I moved it to have it injected through the constructor instead.
I can think of something like this:
$factory = new ProductBuilder();
$factory->buildFromProductId($productId, $wheelCount); //uses modelLookup() internally
$factory->buildFromModelNumber($modelNumber, $wheelCount); //just returns Product()
It is basically creating a class on top of the procedural function, but it does separate the logic of creating the class separately from looking up the mapping.
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
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.