Thoulah
(Mr.42)
June 5, 2020, 4:06pm
1
Im pretty sure the issue below is a case of me not fully understanding PHP 7.4 Typed Properties (aka PEBKAC), but after retrieving values from DB over ActiveRecord
I end up with the following:
object(mister42\models\music\Collection)#90 (10) {
["artist":"mister42\models\music\Collection":private]=>
uninitialized(string)
["created":"mister42\models\music\Collection":private]=>
uninitialized(string)
["id":"mister42\models\music\Collection":private]=>
uninitialized(int)
["image":"mister42\models\music\Collection":private]=>
uninitialized(?string)
["image_color":"mister42\models\music\Collection":private]=>
uninitialized(?string)
["image_override":"mister42\models\music\Collection":private]=>
uninitialized(?string)
["status":"mister42\models\music\Collection":private]=>
uninitialized(string)
["title":"mister42\models\music\Collection":private]=>
uninitialized(string)
["user_id":"mister42\models\music\Collection":private]=>
uninitialized(int)
["year":"mister42\models\music\Collection":private]=>
uninitialized(int)
["_attributes":"yii\db\BaseActiveRecord":private]=>
array(11) {
["id"]=>
int(281014)
["user_id"]=>
int(1)
["artist"]=>
string(12) "Angel Theory"
["year"]=>
string(4) "2004"
["title"]=>
string(15) "Fatal Condition"
["image"]=>
string(7725) "<<binary string deleted>>"
NULL
["image_color"]=>
string(7) "#DFE4E8"
["status"]=>
string(10) "collection"
["created"]=>
string(19) "2019-10-10 20:52:32"
["updated"]=>
string(19) "2020-06-04 22:56:10"
}
["_oldAttributes":"yii\db\BaseActiveRecord":private]=>
array(11) {
["id"]=>
int(281014)
["user_id"]=>
int(1)
["artist"]=>
string(12) "Angel Theory"
["year"]=>
string(4) "2004"
["title"]=>
string(15) "Fatal Condition"
["image"]=>
string(7725) "<<binary string deleted>>"
["image_override"]=>
NULL
["image_color"]=>
string(7) "#DFE4E8"
["status"]=>
string(10) "collection"
["created"]=>
string(19) "2019-10-10 20:52:32"
["updated"]=>
string(19) "2020-06-04 22:56:10"
}
["_related":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
["_relationsDependencies":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
["_errors":"yii\base\Model":private]=>
NULL
["_validators":"yii\base\Model":private]=>
NULL
["_scenario":"yii\base\Model":private]=>
string(7) "default"
["_events":"yii\base\Component":private]=>
array(0) {
}
["_eventWildcards":"yii\base\Component":private]=>
array(0) {
}
["_behaviors":"yii\base\Component":private]=>
array(0) {
}
}
How can I have uninitialized
in the Object and values in _attributes
at the same time? How do I get rid of the uninitialized
after self::findOne
?
samdark
(Alexander Makarov)
June 6, 2020, 7:47am
2
Try updating Yii version.
Thoulah
(Mr.42)
June 6, 2020, 9:15am
3
This is on version 2.0.35.
<?php
namespace mister42\models\music;
use Yii;
class Test extends \yii\db\ActiveRecord
{
private string $artist;
private string $created;
private int $id;
private ?string $image;
private ?string $image_color;
private ?string $image_override;
private string $status;
private string $title;
private string $updated;
private int $user_id;
private int $year;
public static function getEntryLastModified(int $id): int
{
$data = self::find()->where(['id' => $id])->limit(1)->one();
var_dump($data);exit; #shows output above
return isset($data->updated) ? Yii::$app->formatter->asTimestamp($data->updated) : time();
}
public static function tableName(): string
{
return '{{%discogs_collection}}';
}
}
Thoulah
(Mr.42)
June 6, 2020, 10:14am
4
Same result on 2.0.36-dev
mbunyan
(Michael Bunyan)
June 6, 2020, 2:27pm
5
My understanding is that it is thus important to make sure it is given a value before accessing a property. The best ways to enforce this are making sure the property is set in the constructor or giving them default values.
A ‘__construct’ method may be overkill, but you could assign default values which might look like this:
private string $created = "";
private int $id = 0;
private ?string $image = null;
Thoulah
(Mr.42)
June 6, 2020, 2:44pm
6
I don’t think I should have to do that, as that would be a job of the framework. However, Yii still won’t set any values:
private string $title = '';
public static function getEntryLastModified(int $id): int
{
$data = self::find()->where(['id' => $id])->limit(1)->one();
var_dump($data->title);
var_dump($data->attributes['title']);
exit;
}
string(0) ""
string(15) "Fatal Condition"
No error, but an empty string from the assignment.
samdark
(Alexander Makarov)
June 6, 2020, 7:35pm
7
Alright. Now I remember the issue. Here it is:
Thoulah
(Mr.42)
June 6, 2020, 7:44pm
8
I don’t think that issue applied here:
It is talking about user entered data from a form. The error there is at the moment of validating data.
I am trying to retrieve data from database (no need to re-validate), but am now facing empty object values.
I may be wrong, but Yii2 assigns db/ActiveRecord values to _attributes If they are listed in ::attributes(), and to real properties otherwise. So you have to override::attributes() to return empty array… but thus you’ll break getDirtyAttributes and everything that relies on in: smart update and updateAttributes, because they rely on _attributes and _oldAttributes.
So, I see no way to use typed properties in Yii2 the way you want.
Thoulah
(Mr.42)
June 7, 2020, 9:58am
10
I have been able to bring it down to a much more basic issue. Yii2 does not set the value to any property with a set visibility.
public $value;
causes $this->value
to be NULL
object(mister42\models\test\Test)#113 (11) {
["value"]=>
NULL
["_attributes":"yii\db\BaseActiveRecord":private]=>
array(2) {
["id"]=>
int(1)
["value"]=>
string(9) "Yii2 test"
}
["_oldAttributes":"yii\db\BaseActiveRecord":private]=>
array(2) {
["id"]=>
int(1)
["value"]=>
string(9) "Yii2 test"
}
["_related":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
["_relationsDependencies":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
["_errors":"yii\base\Model":private]=>
NULL
["_validators":"yii\base\Model":private]=>
NULL
["_scenario":"yii\base\Model":private]=>
string(7) "default"
["_events":"yii\base\Component":private]=>
array(0) {
}
["_eventWildcards":"yii\base\Component":private]=>
array(0) {
}
["_behaviors":"yii\base\Component":private]=>
array(0) {
}
}
var_dump($data->value); //NULL
public $value = 'test';
causes $this->value
to be string(4) "test"
instead of the assign attribute valueobject(mister42\models\test\Test)#113 (11) {
["value"]=>
string(4) "test"
["_attributes":"yii\db\BaseActiveRecord":private]=>
array(2) {
["id"]=>
int(1)
["value"]=>
string(9) "Yii2 test"
}
["_oldAttributes":"yii\db\BaseActiveRecord":private]=>
array(2) {
["id"]=>
int(1)
["value"]=>
string(9) "Yii2 test"
}
["_related":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
["_relationsDependencies":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
["_errors":"yii\base\Model":private]=>
NULL
["_validators":"yii\base\Model":private]=>
NULL
["_scenario":"yii\base\Model":private]=>
string(7) "default"
["_events":"yii\base\Component":private]=>
array(0) {
}
["_eventWildcards":"yii\base\Component":private]=>
array(0) {
}
["_behaviors":"yii\base\Component":private]=>
array(0) {
}
}
var_dump($data->value); //string(4) "test"
removing the visibility works as expectedobject(mister42\models\test\Test)#113 (10) {
["_attributes":"yii\db\BaseActiveRecord":private]=>
array(2) {
["id"]=>
int(1)
["value"]=>
string(9) "Yii2 test"
}
["_oldAttributes":"yii\db\BaseActiveRecord":private]=>
array(2) {
["id"]=>
int(1)
["value"]=>
string(9) "Yii2 test"
}
["_related":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
["_relationsDependencies":"yii\db\BaseActiveRecord":private]=>
array(0) {
}
["_errors":"yii\base\Model":private]=>
NULL
["_validators":"yii\base\Model":private]=>
NULL
["_scenario":"yii\base\Model":private]=>
string(7) "default"
["_events":"yii\base\Component":private]=>
array(0) {
}
["_eventWildcards":"yii\base\Component":private]=>
array(0) {
}
["_behaviors":"yii\base\Component":private]=>
array(0) {
}
}
var_dump($data->value); //string(9) "Yii2 test"
lubosdz
(Lubosdz)
June 10, 2020, 11:55am
11
If your validator is e.g. string, then add filter for all stringual attributes:
[
[['name'], 'filter', filter => 'trim'], // <- typecast NULL/numeric to string
[['name'], 'string'],
]
Same for numeric - typecast to numeric etc.
IMHO - strict types are putting extra burden to a developer who has to extra pay attention to all incoming types - which may be hard especially for user inputs. Nice example why over-strictyping in PHP is becoming contra-productive.
samdark
(Alexander Makarov)
June 12, 2020, 7:47am
12
@lubosdz it is true for this use-case but strict types are really helpful in all layers not connected with user input.
Thoulah
(Mr.42)
June 12, 2020, 11:46am
13
This is not connected to any user input. That why it is weird to me to have to run a validator to get it working.