Yii2: ActiveRecord values casting to object

Created on 29 Jun 2016  ·  25Comments  ·  Source: yiisoft/yii2

Hi!

I tried to do this with trait, but it looks very bad, so i decide to try request simple feature.
Idea of feature is to cast value from database to object after finding and do reverse when inserting.

Example:
I have column in DB status. This integer enum column (0,1,2).
Right now it returns 0,1,2 as value and i cast it to StatusEnum object thru StatusEnum::getByValue(int).
What if create method getAttributeCasting with return of array with name of attribute as key and class name as value and after each data selecting automatically cast this value to object?

This will not brake any backward compatibility.

under discussion enhancement

Most helpful comment

Sorry, i will answer in russian, Much faster.

:-1: Maybe faster - but locking out the rest of us.

All 25 comments

I like the idea of fine grained type casting.

How do you want config for it to look like?

Isn't this rather about types? How about getAttributeTypes()? Would it make sense to allow for all kinds of casting, like:

public function getAttributeTypes()
{
    return [
        'age' => 'integer',  // you know that this never exceeds int limit

        'status' => [
            'class' => 'Enum',     // we could even have a generic enum class
            'values' => [
                0 => 'closed',
                1 => 'pending',
                2 => 'new',
            ],
        ],

        'foo' => [
            'class' => 'MyFooType',
            'bar' => 'baz',
            ],
        ],
    ];
}

We could have an interface for those DB cast classes.

@mikehaertl yes, it's about types. Having generic Enum interface is OK. Probably abstract class as well.

It may complicate validation though. At least it's less clear what's going on. Like: Is it first casted back to DB type then validated? Or should it be validated before?

Should be validated before, I think.

@samdark for enum we can use https://github.com/Skinka/type-enum

@mikehaertl validation always before inserting. We validate object and cast it back vis getValue just for inserting query.

I wonder a bit, how this would be used in practice. E.g. how would assignments work? Or would they work at all?

// Say, status = 1 in DB
$model = Demo::findOne($id);

// What should this output? 1 or status label ?
echo $model->status;

// Should this work?
$model->status = 0;

@mikehaertl

// Say, status = 1 in DB
$model = Demo::findOne($id);

echo $model->status; // Status object with value = 1

$model->status = 0; // Well, this is subject to voting.

I consider this is a duplication of #9656

@klimov-paul this feature have some connection to #9656, but its little bit different and can be realized w/o #9656

If I tried to achieve your goal I would follow #9656 approach.
You are talking of the interference into the ActiveRecord schema. Which, by the way, is already possible via ColumnSchema::typecast().
Following your path, which should abandon automatic schema detection and let developer to specify it manually including type-casting.

I can see absolutely no problem in actual implementation of this issue or #9656. It is already possible via virtual ActiveRecord properties. All you need is using different names for virtual attributes:

class MyAr extends \yii\db\ActiveRecord
{
    public function getStatusObject()
    {
        return new Status($this->status);
    }
}

You alreay have all necessary tools for your goal.

Sorry, i will answer in russian, Much faster.
@klimov-paul идея в упрощении процедуры кастинга. Да, можно создать кучу геттеров и от них отталкиваться, но это мягко говоря неудобно.
Не надо отказываться от автоматической детекции в целом. Только для полей указанных как кастируемые. Такая штука есть в Java ORM под названием Ebean и показывает себя очень даже не плохо.
Мы всетаки делаем ОО фреймворк и такие плюшки как кастинг не мешают.

Sorry, i will answer in russian, Much faster.

:-1: Maybe faster - but locking out the rest of us.

Translation:

The idea is to simplify type-casting procedure. Of course you can create multiple getters and use them but it's not really convenient. We should not drop automated casting and instead add an ability to customize casting for specific fields. Alike feature is supported in Java ORM called Ebean and I can say it's good. We're creating object-oriented framework and such feature is very handy.

Overall I agree that using setters isn't really convenient since you have to name these differently from the fields which leads to weird naming often.

Извините за французский, но почему бы не сделать метод подобный этому http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#fields()-detail который будет описывать анонимками гетер и сетер для полей. Т.е. при обращении к полю которое есть в базе АР будет его обрабатывать условиями из этого метода.
Как по мне, такого метода очень не хватает. Хотя бы для описания тех же enum полей.

Извините за французский

6 человек против. Давайте уже на английском.

Translation again:

Why can't we introduce a method like http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#fields()-detail which will describe setter and getter for a field via anonymous function i.e. when field is accessed and it's in the DB it will be processed using config defined. I'd love to have such method. At least for enums.

Why can't we introduce a method like http://www.yiiframework.com/doc-2.0/yii-db-baseactiverecord.html#fields()-detail which will describe setter and getter for a field via anonymous function i.e.

Relates to #8316.
There too much possible use cases around ActiveRecord attributes. If we start from this one - where it will end?
If you dislike manual setter/getter so much you can compose your solution into a behavior.

I don't think Yii should provide anything in this field by default. It is better to be implemented as separated extension.

Yeah, it could easily go wrong so while I don't agree that it should not be in the core framework, we should be really careful with it.

Questions open are:

  • When to validate?
  • How about nested objects?
  • How about object collections?
  • How to serialize such models?
  • Is it a good idea to introduce some standard types such as enum?

@klimov-paul
Oh well. but why not give the developer the ability to create a getter for example with the name of the field from the base. it will be on his conscience! for example: field in the database 'status' getter it is possible to make getStatus () instead getStatusTekst ()

but why not give the developer the ability to create a getter for example with the name of the field from the base

https://github.com/yiisoft/yii2/issues/9656#issuecomment-139241911
https://github.com/yiisoft/yii2/issues/2262#issuecomment-33880219

yii\behaviors\AttributeTypecastBehavior introduced at #12067 can be used as a solution.

Was this page helpful?
0 / 5 - 0 ratings