2

I would like to combine 2 functions together, to consolidate code, and to be dynamic depending on how it is used. I do not know if this is possible.

First, lets lay out the basic use. In my example, I have Post and PostCategory models (and CRUD built). You create a category, then create a new post and assign it to the category. A post has the ability to be enabled or disabled. Essentially, you can create some new posts and have them not be viewable to the end users until your ready. One use-case would be a drip feed system, where you could add 100 posts and have one switch to enabled every x days. That is beyond the scope of this.

views\post\_form.php

<div class="post-form">

    <?php $form = ActiveForm::begin(); ?>

    <?= $form->field($model, 'category_id')->dropDownList(
            $model->getPostCategoryConst(),
            ['prompt'=> '- Category -']
        )->label('Category')
    ?>

    <?= $form->field($model, 'name')->textInput(['maxlength' => true]) ?>

    <?= $form->field($model, 'text')->textarea(['rows' => 6]) ?>

    <?= $form->field($model, 'status')->dropDownList(
        $model->getPostStatusConst(),
        ['prompt'=> '- Status -']
    ) ?>

    <div class="form-group">
        <?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
    </div>

    <?php ActiveForm::end(); ?>

</div>

Notice the drop down lists for category_id and status and the functions they call

\common\models\Post.php

const STATUS_ENABLED = 1;
const STATUS_DISABLED = 0;

public function getCategory()
{
    return $this->hasOne(PostCategory::className(), ['id' => 'category_id']);
}

/* -- Added -- */

public function getPostCategoryConst()
{
    return ArrayHelper::map(PostCategory::find()->orderBy('name DESC')->all(), 'id', 'name');
}

public function getPostStatusConst()
{
    return [
        self::STATUS_DISABLED => 'Disabled',
        self::STATUS_ENABLED => 'Enabled',
    ];
}

Now this works just fine :) However, I don't like using get as in getPostStatusConst() as it isn't accessed like $model->postStatusConst similar to how relations are with the "getter".

I would like to use these as "getters" as well though. In the index and view, it would be nice to also call the same functions. Instead of returning an array, to return a "nice name" such as "Enabled" or "Disabled"

For the sake of this, I won't rename the function, as I don't want to add any more confusion.

views\post\view.php

<?= DetailView::widget([
    'model' => $model,
    'attributes' => [
        'id',
        'category.name',
        'name',
        'text:ntext',
        'postStatusConst',  // <-- Calls getPostStatusConst()
        'created_at:datetime',
        'updated_at:datetime',
    ],
]) ?>

Notice postStatusConst is the same function as we used in _form for our create action. In the _form, it needed to return an array for the drop down list. In our view, it just needs to return a nice name such as Enabled or Disabled.


I Tried

I tried this function in the Post model:

public function getPostStatusConst()
{
    if ( isset($this) ) {
        return ($this->status === self::STATUS_ENABLED) ? 'Enabled' : 'Disabled';
    }

    return [
        self::STATUS_DISABLED => 'Disabled',
        self::STATUS_ENABLED => 'Enabled',
    ];
}

That obviously didn't work :) I didn't expect it to, because I know $this references itself in a class. It just shows what I am going for.

In relations, the hasOne() seems to know whether we are using it as a direct call (Post::getCategory) or inline ($model->category->name)..

Question

Is it possible to have getPostStatusConst() do the same? To use as $model->postStatusConst to display Enabled or Disabled nicely, or as Post::getPostStatusConst() to get the array for the drop down.

Wade
  • 3,757
  • 2
  • 32
  • 51

2 Answers2

0

It is possible but really not worth all the changes in code. You would have to override magic __get() method and think about some way to store and access both returns in one structure.

I would leave getPostStatusConst() with current status name and add other method (even static) with the list of statuses for dropdown list.

Bizley
  • 17,392
  • 5
  • 49
  • 59
  • Thank you for your time. I agree, that what you said isn't worth it. In past projects I have used 2 separate functions. As I go along, I try to do things better, cooler, and more efficient :) I thought I read somewhere that relations, such as built with Gii (for example hasOne()) knows the difference between how it is used. I skimmed the code, but haven't jumped through many hoops because each file, references another, that references another lol. – Wade Aug 04 '16 at 00:02
  • Actually, I was really close :) I solved it! I will mark my answer when SO lets me (2 days I think). Until then, I am open to any other solutions. Maybe some underlying Yii function, or a better way. – Wade Aug 04 '16 at 00:33
  • It's nice hack but triggers error/warning depending on PHP version and configuration. When answering I was thinking about the same solution that is available for relation methods and attributes in Yii 2. – Bizley Aug 04 '16 at 05:35
  • It does throw the error as per my other comment. I was thinking about the same thing, it works with relations. This should be replicable. I went through the source but couldn't find where they pull it off. – Wade Aug 05 '16 at 05:29
  • https://github.com/yiisoft/yii2/blob/2.0.9/framework/db/BaseActiveRecord.php#L253 – Bizley Aug 05 '16 at 05:52
0

I was pretty close. I wasn't thinking PHP OOP lines, but more Yii. A few Google searches and I slapped my forehead. When using frameworks, you forget your even writing in PHP sometimes ;)

public function getPostStatus()
{
    if ( isset($this) && get_class($this) == __CLASS__) {
        // not static
        return ($this->status === self::STATUS_ENABLED) ? 'Enabled' : 'Disabled';
    }

    return [
        self::STATUS_DISABLED => 'Disabled',
        self::STATUS_ENABLED => 'Enabled',
    ];
}

I renamed the function so that it makes more sense.

It works everywhere. Lets see the index of my CRUD:

views\post\index.php

    <?= GridView::widget([
    'dataProvider' => $dataProvider,
    'filterModel' => $searchModel,
    'columns' => [
        ['class' => 'yii\grid\SerialColumn'],

        'id',
        'category.name',
        'name',
        //'text:ntext',
        'postStatus',
        // 'created_at',
        // 'updated_at',

        ['class' => 'yii\grid\ActionColumn'],
    ],
]); ?>

views\post\view.php

<?= DetailView::widget([
    'model' => $model,
    'attributes' => [
        'id',
        'category.name',
        'name',
        'text:ntext',
        'postStatus',
        'created_at:datetime',
        'updated_at:datetime',
    ],
]) ?>

and views\post\_form.php

<?= $form->field($model, 'status')->dropDownList(
    Post::getPostStatus(),
    ['prompt'=> '- Status -']
) ?>

All those cases seem to work fine. Anyone have any cases this would not work?

Wade
  • 3,757
  • 2
  • 32
  • 51
  • Are you not getting error `Strict standards: Non-static method Post::getPostStatus() should not be called statically`? Depending on your configuration and/or PHP version your logs (and what is worse, your output) might get messy. – Bizley Aug 04 '16 at 05:33
  • Your right. I re-installed xampp a few weeks ago and forgot to change the error reporting to E_ALL. It was excluding strict and deprecated. So while this does work just fine, strict will complain about it. – Wade Aug 05 '16 at 05:26