Access to undefined property exception thrown in setter function when I fetch record by findFirst($id) method
The findFirst() model function internally calling custom setter functions written in the model. But it throws exception when it finds accessing other property of the same model which is $this->property_five next to $this->property_four in mysql table
-------------------------------------------
id | property_four | property_five
-------------------------------------------
1 | Phalcon 3.4.4 | PHP 7.3
-------------------------------------------
E_USER_NOTICE: Access to undefined property Example::property_five in /path/to/Example.php on line xxx
Steps to reproduce the behavior:
class Example extends \Phalcon\Mvc\Model
{
...
public function setPropertyFour()
{
$this->property_four = $this->property_five;
}
}
Example::findFirst($id)
Details
@manojwp Did you setup column map?
@manojwp Did you setup column map?
No. Is it not created automatically ? Both column names and model property names are same.
Is there a way to disable setter functions to be called only while fetching records like findFirst ?
No, because you are working inside raw PHP Object class, if you didn't declared it, then it will throw errors/notices. Magic happens after, with findFirst(). which picks Model class and add all necessary properties.
No, because you are working inside raw PHP Object class, if you didn't declared it, then it will throw errors/notices. Magic happens after, with findFirst(). which picks Model class and add all necessary properties.
it's not throwing exception on all setters, it does only when it finds accessing other properties inside setter function..
// This setter works
public function setPropertyThree()
{
$this->property_three = 'some value';
}
// This one fails
public function setPropertyFour()
{
$this->property_four = $this->property_five;
// here $this->property_five is next to property_four in mysql table.
// Able to access properties only which are prior to property_four and whatever comes after property_four are not accessible
}
// This one works
public function setPropertyFive()
{
$this->property_five= $this->property_four;
}
Just declare it inside model class, without value
class Example extends \Phalcon\Mvc\Model
{
public $property_three;
public $property_four;
public $property_five;
}
Just declare it inside model class, without value
class Example extends \Phalcon\Mvc\Model { public $property_three; public $property_four; public $property_five; }
I've too many columns in a table can't declare all. Moved property_five prior to property_four in the table and it worked. Not sure why property_five was not set in the model anyways it auto-sets those properties, it should auto-set all the properties before calling setter functions of the model that was the expected behaviour
You can generate models with properties with https://github.com/phalcon/phalcon-devtools
You can generate models with properties with https://github.com/phalcon/phalcon-devtools
I am migrating phalcon 1.3.4 to 3.4.4 so not sure this tool would help
Might help, as it supports by Phalcon versions sinse 1.3.x

Might help, as it supports by Phalcon versions sinse
1.3.x
Is it possible to declare properties for existing models by command ?
Yes, it will generate similar class as this one:
https://github.com/phalcon/phalcon-devtools/blob/4.0.x/tests/_data/console/app/models/files/TestModel.php
Yes, it will generate similar class as this one:
https://github.com/phalcon/phalcon-devtools/blob/4.0.x/tests/_data/console/app/models/files/TestModel.php
It was working with old version of phalcon 1.3.4, with latest it fails, anyways thank you so much for your help appreciate it
So if you access this property outside of $this context it works? Looks like maybe the __get is not called when accessing property from $this context in zephir but this would need to be checked, but i doubt this is an issue.
So if you access this property outside of
$thiscontext it works? Looks like maybe the__getis not called when accessing property from$thiscontext in zephir but this would need to be checked, but i doubt this is an issue.
No, because you are working inside raw PHP Object class, if you didn't declared it, then it will throw errors/notices. Magic happens after, with findFirst(). which picks Model class and add all necessary properties.
it's not throwing exception on all setters, it does only when it finds accessing other properties inside setter function..
// This setter works public function setPropertyThree() { $this->property_three = 'some value'; } // This one fails public function setPropertyFour() { $this->property_four = $this->property_five; // here $this->property_five is next to property_four in mysql table. // Able to access properties only which are prior to property_four and whatever comes after property_four are not accessible } // This one works public function setPropertyFive() { $this->property_five= $this->property_four; }
@Jurigag this is the case
Just declare it inside model class, without value
class Example extends \Phalcon\Mvc\Model { public $property_three; public $property_four; public $property_five; }I've too many columns in a table can't declare all. Moved property_five prior to property_four in the table and it worked. Not sure why property_five was not set in the model anyways it auto-sets those properties, it should auto-set all the properties before calling setter functions of the model that was the expected behaviour
@Jurigag this is what i tried
The issue described is not a bug. It is actually a side effect on how the whole application works. You will notice the same thing with pure PHP also.
I copied the model like so:
class PropertySetter extends Model
{
public function initialize()
{
$this->setSource('co_property_setter');
}
public function setPropertyFour()
{
$this->property_four = $this->property_five;
}
}
The table is:
create table co_property_setter
(
id int(10) auto_increment primary key,
property_one varchar(100) null,
property_two varchar(100) null,
property_three varchar(100) null,
property_four varchar(100) null,
property_five varchar(100) null
);
When I get one record from the table using findFirst, Phalcon does the following:
The issue happens when the data is transferred or assigned to the model, in particular the cloneResultsetMap method.
There is a loop which traverses the database resultset, and depending on the column map (if defined) and a few other conditions, the loop will assign the data to the model
let instance->{key} = value;
When this line is executed, the __set method is immediately called. The reason is because the property does not exist in the model (see model definition above). The code in the __set method also checks if my assignment is a possible setter in the model. For the first few assignments, nothing exists so no setters are called.
Remember we are still in the loop that is assigning data to the model.
So when the loop reaches property_four it :
__set__set checks if there is a setter and calls itproperty_five to property_fourproperty_five is not assigned yet so __get is calledBy the above, we have successfully created a circular reference or an impossible solution to resolve.
I experimented with assigning a temporary flag before the population from database to model happens so that I do not invoke any setters but then that stopped the setters from running. With a bit of experimentation I managed to get the temporary variable to allow or not the setters to run and then leave everything as is. This created more problems than what we are trying to solve. In addition, to make that theory work, I had to run the loop twice, one without the setters and one with them, which reduces performance.
We will not be fixing this since it does impact performance.
Potential solutions:
afterFetch event to manipulate property_four with property_fiveThe issue described is not a bug. It is actually a side effect on how the whole application works. You will notice the same thing with pure PHP also.
I copied the model like so:
class PropertySetter extends Model { public function initialize() { $this->setSource('co_property_setter'); } public function setPropertyFour() { $this->property_four = $this->property_five; } }The table is:
create table co_property_setter ( id int(10) auto_increment primary key, property_one varchar(100) null, property_two varchar(100) null, property_three varchar(100) null, property_four varchar(100) null, property_five varchar(100) null );When I get one record from the table using
findFirst, Phalcon does the following:
- Construct the PHQL statement
- Run the SQL command against the database
- Get the results
- Populate/transfer the results from the db resultset to the model class.
The issue happens when the data is transferred or assigned to the model, in particular the
cloneResultsetMapmethod.There is a loop which traverses the database resultset, and depending on the column map (if defined) and a few other conditions, the loop will assign the data to the model
let instance->{key} = value;When this line is executed, the
__setmethod is immediately called. The reason is because the property does not exist in the model (see model definition above). The code in the__setmethod also checks if my assignment is a possible setter in the model. For the first few assignments, nothing exists so no setters are called.Remember we are still in the loop that is assigning data to the model.
So when the loop reaches
property_fourit :
- tries to assign the value
- calls
__set__setchecks if there is a setter and calls it- the setter is trying to assign
property_fivetoproperty_fourproperty_fiveis not assigned yet so__getis called- the property is not found -> exception is thrown.
By the above, we have successfully created a circular reference or an impossible solution to resolve.
I experimented with assigning a temporary flag before the population from database to model happens so that I do not invoke any setters but then that stopped the setters from running. With a bit of experimentation I managed to get the temporary variable to allow or not the setters to run and then leave everything as is. This created more problems than what we are trying to solve. In addition, to make that theory work, I had to run the loop twice, one without the setters and one with them, which reduces performance.
We will not be fixing this since it does impact performance.
Potential solutions:
- Create a column map
- Add properties as the fields in your model
- Change if possible the setter name and utilize the
afterFetchevent to manipulateproperty_fourwithproperty_five
Why not put the values stored on the attributes in the model into a private attributes array?
__get reads from attributes
__set writes to attributes