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