Difference between $model->attributes and $model->setAttributes() in Yii - php

Whenever I use $model->attributes=$_POST['Users'] ,it saves Data from User form.
When I use $model->setAttributes($_POST['Users']),it also saves Data from User form.
So please can anyone clarify the difference between the two codes ?

With $this->setAttributes() you can assign unsafe attributes, using $this->attributes you cant.
Assigning unsafe attributes:
$this->setAttributes($_POST['Model'], false);
More info in: http://www.yiiframework.com/doc/api/1.1/CModel/#setAttributes-detail

As stated in the Yii wiki, you can use any of these. With $model->attributes you set the variable directly. With $model->setAttributes() you set the variable through a so called 'setter method'.
http://www.yiiframework.com/wiki/167/understanding-virtual-attributes-and-get-set-methods/#hh1
I would use the setter method instead of directly calling the variable, as you can add a line in your setter method, and it would apply to all of its calls, and it would save you from a lot of headache in the future.
Example:
class Model {
public $attributes;
public function setAttributes($attributes) {
$this->attributes = $attributes;
}
public function getAttributes() {
return $this->attributes;
}
}
$model = new Model();
$model->setAttributes("Foo");
echo $model->getAttributes();
$model->setAttributes("Bar");
echo $model->getAttributes();
So, now if you would like to make an additional operation on the attribute, you could add it to the setAttributes() method, and instead of changing two lines of code, you could change only one.
Example:
class Model {
public $attributes;
public function setAttributes($attributes) {
$this->attributes = $attributes . "-Bar";
}
public function getAttributes() {
return $this->attributes;
}
}
$model = new Model();
$model->setAttributes("Foo");
echo $model->getAttributes();
$model->setAttributes("Bar");
echo $model->getAttributes();
Now scale this up to a level, when it would be inconvenient to change thousands of lines of code, instead of changing a couple of setter methods.

There is absolutely no difference.
When you try to assign a property that is not defined as a PHP class property (such as attributes here) on a component, Yii by convention calls the similarly-named setter method setAttributes instead. If no such method exists an exception is thrown. Since a Yii model is a component and models do not have an attributes property, the setter method is called even when you use the first form.
All of this is also explained in detail in the manual.

$model->attributes=$_POST['Users']// means setting value of property directly while
$model->setAttributes($_POST['Users']) //is method or function which is indirectly set value of $model->attributes property;
Lets take an example
class Model{
public $attributes;
public function setAttributes($att){
$this->attributes=$att;
}
}
//Now the value of $attributes can be set by two way
$model = new Model();
$model->attributes=$value; // 1st way
$model->setAttributes($value); //2nd way

There is no difference. array_merge used to merge attributes, if set later

Related

PHP Have Object Return Different Object

In a few different places I call:
$model=NEW MakeCall($form);
I have updated the MakeCall class with several changes that I want to take affect after a given date. I renamed the original class MakeCallOld
How can I leave my calls to:
$model=NEW MakeCall($form);
intact and within MakeCall do this:
class MakeCall
{ ...
public function __construct($form)
{
//Use Legacy MakeCallOld for stuff before 2016-10-01
if ($form['date']<'2016-10-01') {
$object=NEW MakeCallOld($form);
return $object;
}
$this->perform several functions and set variables...
This currently just returns an empty object of class MakeCallOld but it does not appear to run the constructor in MakeCallOld as all properties are empty. I would just like the entire object of MakeCallOld dropped into $model.
What you need is a static factory constructor. This is the way you should be doing it to add initialization logic or switch constructors depending on the argument.
class MakeCall
{
public function __construct($form)
{
$this->form = $form;
}
public function showForm(){
echo $this->form;
}
public static function create($form){
//put logic for picking object or whatever here!
$object = new MakeCall($form);
//Do more initializing if you want here!
return $object;
}
}
$form = "asdasd";
$model= MakeCall::create($form);
$model->showForm();
Constructors don't have a return value, so by saying return $object you are simply ending the control flow there and doing nothing else, never reaching "perform several functions and set variables". Depending on your structure you should consider making MakeCall inherit from MakeCallOld, then you can simply call parent::__construct(); in MakeCall's constructor.

Accessing a database value in a models constructor in Laravel 5.2

I'm attempting to fetch, convert and save a value in a models' constructor in Laravel 5.2. The reason being that it's saved in the database as hex, and I need to convert it to binary pretty often, and would like to do it once and save the result in a class attribute. But I can't seem to be able to fetch the value from $this in the constructor.
Here's a excerpt of what I'm working with, guid is a field in my table.
class Person extends Model {
private $bGuid = null;
public function __construct(array $attributes = []) {
parent::__construct($attributes);
$this->ad = Adldap::getProvider('default');
$this->bGuid = hex2bin($this->guid);
}
public function getName(){
$query = $this->ad->search()->select('cn')->findBy('objectGUID', $this->bGuid);
return $query['attributes']['cn'][0];
}
}
The $this->ad attribute executes as expected, but $this->bGuid does not. Some debugging shows that $this->guid when referenced in the constructor returns null. While if referenced in the getName() method directly works just fine.
My intermediate solution is creating a new function and just call $this->getbGuid(), thus making me a bit more satisfied with the DRY-ness, but it still has to convert it each time it is called.
I would appreciate it if anyone could tell me what's going wrong, so I could improve the code :)
Try to override another method from Model: newFromBuilder().
This is the one that is executed once the data is retrieved from the DB, not the __construct() one:
class Person extends Model {
private $bGuid = null;
public function newFromBuilder($attributes = [], $connection = null)
{
$model = parent::newFromBuilder($attributes, $connection);
$model->bGuid = hex2bin($model->guid);
return $model;
}
}
Note, that inside the overridden method you refer to the object as $model (instead of $this), and it has to return the $model object at the end.

Static variable assignment in descendent bubbles up to parent?

I've run into a problem and I'm not sure if this is just normal behaviour or if I wrote something wrong. I have a method in my base class that applies a global filter to a given class by way of creating a proxy for all new instances of that particular class. The way I planned to go about it is as follows:
Attach static $global_filter (the proxy) to the class I want to be filtered, which extends the base class object
Via my loading mechanism, return the proxy instead of the actual class upon new instantiations (which will mask method calls and apply filters accordingly)
However, I am getting stuck in step 1 and it seems that when I try to assign static $global_filter to the descendent class I want filtered, my base class object also gets the same assignment, which breaks everything else that extends from it.
Please see below for relevant code:
class object {
public static $global_filter;
public function _filterGlobal($class, $method, $callback) {
if ( !is_object($class::$global_filter) ) {
$class::$global_filter = new filterable(null);
# Replace the object being called with the new proxy.
}
var_dump($class);
var_dump($class::$global_filter); // `filterable`
var_dump(\core\blueprint\object::$global_filter); // Returns same as line above
die();
return $class::$global_filter->_add($method, $callback);
}
}
Both $class::$global_filter and \core\blueprint\object::$global_filter (the base class) are returning same instance. Whereas I expected object::$global_filter to be null.
I'm not using late static binding in order to preserve consistency (both single-object filters and global filters are called much in the same way non-statically).
This question seems relevant
Any help will be much appreciated :)
Edit, full example
This would be a concrete class, which extends model which extends object
<?php
use core\blueprint\model;
class modelMock extends model {
protected $schema = array();
public function method($test) {
return $test;
}
}
This would be another object (e.g a controller), which extends object aswell. It applies a filter to all new instances of model
<?php
use core\blueprint\object;
class objectMock extends object {
public function applyFilters() {
$this->_filterGlobal('core\blueprint\model', 'method', function($self, $chain) {
$chain->params[0] = 'new param'; // adjust the paramters
return $chain->next();
});
}
}
when I try to assign static $global_filter to the descendent class I want filtered, my base class object also gets the same assignment
Yes, indeed this happens. A static property in essence is a global variable, constrained within the class's namespace. Running into problems with global variables is often an indication you're not using the best solution.
To solve your problem, you could make the filter a (non-static) property:
$class->$filter = new Whatever();
But as always, there's more roads that lead to Rome, and I would advise you to look for alterative ways to do it.
I don't know if this is a help for you:
class a {
public static $type;
public static function setType($class, $newType) {
$class::$type = $newType;
var_dump($class::$type);
}
}
class b {
public static $type = 'myType';
}
var_dump(b::$type);
a::setType('b', 'yourType');
var_dump(a::$type);
May be you have not defined the static property to the concrete class.
Thanks everyone for you help, I spent some time on it this morning and managed to solve my problem. It's a bit of a workaround but here's how it goes:
public function _filterGlobal($class, $method, $callback) {
if ( !is_object($class::$global_filter[$class]) ) {
$class::$global_filter[$class] = new filterable(null);
# Replace the object being called with the new proxy.
}
return $class::$global_filter[$class]->_add($method, $callback);
}
So basically in order to get unique static variables working in child classes without having to explicitly define them, you can use an array that stores the child's class name as a key and then access these variables via a getter.

When object property is asked first time gets data, and then just returns it - is it possible?

I need some data from the object.
I don't want these data to be loaded in class construction, because it is db heavy.
I don't want to load it more than once in a page.
I don't want to remember was it loaded already, or not.
$object->data // should be loaded in construction
$data = $object->get_data() // ok, but I need to remember was is got already, or not.
Is there a way to use $object->data, if it is asked first time, it actually gets data and returned it. And when I ask it after this, it just returns old data.
If there is no way, I will just use $data = $object->get_data(). But maybe I'm missing something.
This is usually solved using "lazy loading" - the property itself is backed using a private field, which gets initialized to some magic value (e.g. null) in the constructor, and gets filled the first time the getter gets called. After that, the getter returns the already-loaded value. Example:
class Foobar {
private $_lazy;
public function __construct() {
$this->_lazy = null;
}
public function __get($key) {
switch ($key) {
case 'lazy':
if ($this->_lazy === null)
$this->loadLazy();
return $this->_lazy;
}
}
private function loadLazy() {
$this->_lazy = rand();
}
}
Thing that you talking about is called Lazy Loading. You should implement that in method get_data(). If you wanna use it as property, not method, you must use PHP's magic __get method and return your data when accessing that data property.
But I recommend using method - it's more explict.
Well, you can do this
//create an object
class Foo{
//give some attributes
public $attr1;
public $attr2;
public $attr3;
public $attr4;
....
....
//create a function to load data
public function foofunction()
{
//and set the attrs
$this->attr1 = $somevalue;
$this->attr2 = $somevalue;
$this->attr3 = $somevalue;
//...
....
}
}
and you in your page
//create an object
$foo = new Foo();
//fetch data which will instantiate the attrs
$foo->foofunction();
//and you can use any attr at any time
echo $foo->attr1;
echo $foo->attr2;
//and this attr necessarily does not have to string, or int or ..
//it can be anything
Object has a property - flag, that indicates if the data have been asked before.
It's lazy loading
// simple implementation
public function get_data() {
if (is_null($this->_data)) {
$this->_data = $db->query();
}
return $this->_data;
}

Creating Object instance from Posted data - PHP

What is the best way of creating and populating an Object from values passed in from a form?
If for example, i have a class Car with properties Colour, Model, Make, Year and a method Save, which will either insert or update the record.
I then have a form that has fields for all these values and it is submitted. I want to create an instance of class Car with the posted values and then call the Save method. All good and well.
But what is the best way of assigning the posted values to the objects internal properties. Assuming this is a simplified scenario and the actual situation would have many more properties, making individual Set calls long-winded.
Is it best to simply call the Set method for each one? Or pass in an array to a method (or the constructor) which then calls the Set methods? Or some other way?
Any advice on best practices is appreciated
Cheers
Stuart
If the properties are public:
foreach($_POST as $key => $value)
$obj->$key = $value;
If they require setters:
foreach($_POST as $key => $value) {
$set = 'Set' . $key;
$obj->$set($value);
}
You can use a bit of Reflection magic:
public function loadFromArray($array) {
$class = new ReflectionClass(get_class($this));
$props = $class->getProperties();
foreach($props as $p) {
if (isset($array[$p->getName()])
$p->setValue($this, $array[$p->getName]);
}
}
You can implement this method in a base class and make all yout object inherit from that, so you have this functionality in every object without repeating yourself in any class.
I would implement the magic function __set_state(), as it is intended for exactly this use case. There are multiple benefits of putting the implementation into that method:
It is very well defined, what this function does (It is defined in the PHP docs, in contrast to your own source code)
You can access private members within the function
Objects dumped with var_export() will be automatically reconstructed using this function.
EDIT As requested in the comments:
class A {
private $member = 1000;
public static function test(A $a) {
echo $a->member;
}
}
echo A::test(new A()); // outputs 1000
EDIT Fulfilling another request from the comments:
You cannot know on which class a static method was called unless you are using php 5.3, in which the required feature (late static binding) was introduced. What you can do in emulating get_called_class() by analyzing the current stack (using debug_backtrace()). If you have a working get_called_class() function, you can do the following:
class BaseClass {
public static function __set_state($state) {
$class = get_called_class();
// Assumption: Constructor can be invoked without parameters!
$object = new $class();
foreach (get_class_vars($class) as $member) {
if (!isset($state[$member])) {
// Member was not provided, you can choose to ignore this case
throw new Exception("$class::$member was not provided");
} else {
// Set member directly
// Assumption: members are all public or protected
$object->$member = $state[$member];
// Or use the setter-approach given by Chaos.
$setter = 'set' . ucfirst($member);
$object->setter($state[$member]);
}
}
return $object;
}
}
Well, you can convert the post array to an object in one step...
$mypostobject = (object) $_POST;
echo $mypostobject->Colour;
EDIT: added link to PHP docs. http://uk.php.net/manual/en/language.types.object.php
If you want to do this with your own class of object, then some kind of constructor or function that takes the post array and set up your class would be in order.
class MyCar {
public function __construct($postdata)
{
$this->Colour = $postdata['Colour'];
// etc
}
};
$car = new MyCar($_POST);
In case the object that you're posting may become more complex than a flat list of properties in the future (think nesting, arrays as values etc.), you could first create a JavaScript object on the client side and post it as JSON inside a single parameter. Then you can simply deserialize the JSON string into a PHP object. PHP 5+ has built-in support for JSON deserialization. Using JSON would allow you to be flexible in how complex your object can be.
create a constructor which will take all the attributes
class Car {
// private members …
public function __construct($color, $type, $model, $make) {
// assign your values here
}
// other methods
};
if you are dealing with associative arrays, you can also use a constructor with an array as argument:
public function __construct($car_properties) {}
and then loop your properties with
foreach($car_properties as $property => $value) {
if(isset($value)) $this->property = $value
}
be sure to sanitize/validate your input beforehand! (can’t stress this often enough)

Categories