How to get at fields in related tables in CREATE form

Here is my scenario:

I have the following tables:

Employees

ServiceOrders

DriversActivity.

The relationships are as follows:

1 DriversActivty can have 0 or 1 Employees. (A DriversActivity refers to an employee that is a driver that is either active or inactive depending on if the ‘end_time’ field in the table is not NULL. You can think of a DriversActivity record as a shift, with a start_time and end_time. If the end_time is not set it means the driver is active).

1 DriversActivity can have many ServiceOrders.

Here is the a representation of the ERD for these 3 tables:

7400

drivers_activity_to_employees.png

So here is what I am struggling with. And this is not just for this relationship set, but for dealing with relations in Yii2 in general. I think Yii2 is pretty amazing, but the documentation doesn’t really provide enough context to be able to just run with things. My big problem is that the models as generated by Gii are not enough for working with relations that span more than one table, so in both the search forms (gridview) and the create forms (_form.php in the views) I can’t figure out how to do the damn joins to get the queries that I need.

For these 3 tables I have here, I want to – within the ‘CREATE’ view for a ServiceOrder, to provide the user with a dropdown that lets them select drivers that have no end_time set.

The problem is that the relationship from the service order to the drivers_activity table is through the employees table. So I can’t figure out how to get to the ‘end_time’ in the drivers activity table from the service_orders CREATE view.

I hope I am making sense here. I have spent days and days trying to figure this out to no avail.

Here is the code in the _form.php file. I am using a widget that lets me provide a dropdown for fields that are foreign keys to related tables (kartik\select2\Select2).

For the Drivers selection, I want to filter the query so that only drivers from the DriversActivity table that have an ‘end_time’ field of NULL are displayed. I don’t know how to connect the table relation. The service_orders_table is connected to the employees table via ‘id’ field, and these id’s also match with ‘drivers_id’ field in the DriversActivity table. Since the DriversActivity table is not connected to the service_orders table via its model file, I can’t figure how how or where to do it.


use yii\helpers\Html;

use yii\helpers\ArrayHelper;

use yii\widgets\ActiveForm;


// Our own models

// use app\models\Trucks;

use app\models\Employees; // for connecting with drivers from the employee table

use app\models\BatteryTypes;

use app\models\PayTypes;

use app\models\Regions;

use app\models\DriversActivity;




// Widgets

use kartik\select2\Select2;


/* @var $this yii\web\View */

/* @var $model app\models\ServiceOrders */

/* @var $form yii\widgets\ActiveForm */

?>


<div class="service-orders-form">


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


    <?= $form->field($model, 'SVONumber')->textInput() ?>


    <?= $form->field($model, 'date')->textInput() ?>




 [b]   <?php // Driver selection

        echo $form->field($model, 'driver_id')->widget(Select2::classname(), [

            'data' => ArrayHelper::map(DriversActivity::find()

                ->asArray()->all(),

                'id',

                function($model, $defaultValue) {

               	return $model['first'].' '.$model['last'];

                }

            ),

            'language' => 'en',

            'options' => ['placeholder' => 'Select Driver'],

            'pluginOptions' => [

                'allowClear' => true

            ],

        ]);

    ?>

[/b]

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


    <?php // Pay Type selection

        echo $form->field($model, 'pay_type_id')->widget(Select2::classname(), [

            'data' => ArrayHelper::map(PayTypes::find()->asArray()->all(),

                'id',

                function($model, $defaultValue) {

               	return $model['pay_type'];

                }

            ),

            'language' => 'en',

            'options' => ['placeholder' => 'Select Pay Type'],

            'pluginOptions' => [

                'allowClear' => true

            ],

        ]);

    ?>


    <?php // Battery Type selection

        echo $form->field($model, 'battery_type_id')->widget(Select2::classname(), [

            'data' => ArrayHelper::map(BatteryTypes::find()->asArray()->all(),

                'id',

                function($model, $defaultValue) {

               	return $model['manufacturer'] . ' - ' . $model['model'];

                }

            ),

            'language' => 'en',

            'options' => ['placeholder' => 'Select Battery Type'],

            'pluginOptions' => [

                'allowClear' => true

            ],

        ]);

    ?>




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


    <?php // Region selection

        echo $form->field($model, 'region_id')->widget(Select2::classname(), [

            'data' => ArrayHelper::map(Regions::find()->asArray()->all(),

                'id',

                function($model, $defaultValue) {

               	return $model['region_number'] . ' - ' . $model['region_name'];

                }

            ),

            'language' => 'en',

            'options' => ['placeholder' => 'Select Region For This SVO'],

            'pluginOptions' => [

                'allowClear' => true

            ],

        ]);

    ?>


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


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


    <div class="form-group">

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

    </div>


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


</div>




I am close to desparate now on this. Please, if you are a Yii2 pro, help me out here. I’m ready to provide any information you need to help me solve this. This is also quite urgent for me. If I can’t figure out how to easily work with relations across multiple tables related by foreign keys with Yii, I may have to completely drop Yii2 and use something else. That’s how urgent/desparate I am on this one.

Many thanks in advance.

psr123

I should clarify that my problem is not with the SQL needed, but in how to incorporate this in Yii?

Do I need to create another model that spans the 3 tables and somehow use that in the create view (_form.php) for service_orders?

Do I need to modify the ServiceOrders.php model to somehow extend the relations to hit the DriversActivity table as well? How would I make the fields from the DriversActivity table to visible/accessible to ServicesOrders?

Do I need to somehow deal with these relations inside the view file, in the dropdown definition?


 echo $form->field($model, 'driver_id')->widget(Select2::classname(), [

            'data' => ArrayHelper::map(DriversActivity::find()

                ->asArray()->all(),  // SOMEHOW JOIN THIS WITH THE EMPLOYEES TABLE TO BE ABLE TO DISPLAY NEEDED FIELDS?

                'id',

                function($model, $defaultValue) {

                   return $model['first'].' '.$model['last'];

                }

            )

What should I do here?

Thanks

psr123

Here is where it’s breaking:

return $model[‘first’].’ '.$model[‘last’];

It can’t find the fields ‘first’ and ‘last’ because those are in the Employees table. The driver_id field is the foreign key to both the DriversActivty table table and the Employees table.

How can I join with the employees table? I have tried adding a relation to the DriversActivity model to the employees table, but that didn’t resolve it.

Seriously, nobody in Yii community can help me with this? Getting super discouraged…

psr123

You put "URGENT" in the title - that will not sit well with most people.

You are also getting "super discouraged" after only one and a half hour.

Do you think we are a bunch of monkeys without any social life, just waiting to answer your questions?

I still don’t have time to look into your issue - hope that someone else has time, though - but please reconsider the situation.

It is also Pentecost in my part of the world - holiday - that could also explain things.

That, and the fact that the Yii forum is not exactly overrun with people like it used to.

((Hopefully, a forum upgrade will fix that))

I did not mean to offend you or anyone else. I definitely don’t think you or anyone else is a monkey without a social life. I put urgent in the title because truthfully for me it is urgent. Also, I am feeling discouraged because I have spent several days trying to resolve this on my own to no avail, before I asked for help here. The Yii2 API documentation is pretty good, but what I have had trouble with specifically is working with the models where relations are more than one degree removed from the specific model one is working on. I can’t find any examples pertaining to my issue.

Anyway, going to keep at it and hopefully I will get to a solution soon, but I still would appreciate any help you or anyone else can provide.

Thanks,

psr123

Hi, i don’t understand exactly what you want to acomplish but it seems to me that you’re trying to work with relations via a junction table. In this case, your service_orders model is related to your drivers_activity model via the employees model. So, if you wanna access the data from this relation, first you should define the relation the way Yii2 defines them.

Relations are defined in each model class and you can access them from any instance of that model after that.

To be able to guide you more, can you post all the models involved in this? (code for each one)

Thank you Gonzalo!

So I figured it out. Really had to dig deep into understanding how relations work with ActiveQuery. I defined all of the relations exactly as you described them in each respective model, then was able to create narrow the query to get the DriversActivity’s that I needed, joined with Employees. I need this selection to be available in the _form.php for creating new service_orders. Here’s how I did it.


   <?php // Driver selection

        echo $form->field($model, 'driver_id')->widget(Select2::classname(), [

            'data' => ArrayHelper::map(Employees::find()

                ->joinWith('driversActivity', true, 'INNER JOIN')

                ->where(['end_time' => NULL])

                ->asArray()->all(),

                'id',

                function($model, $defaultValue) {

                   return $model['first'].' '.$model['last'];

                }

            ),

            'language' => 'en',

            'options' => ['placeholder' => 'Select Driver'],

            'pluginOptions' => [

                'allowClear' => true

            ],

        ]);

    ?>

Checking for end_time of NULL let’s me know that the driver is still active. ‘first’ and ‘last’ attributes are from the Employees model. The piece that was missing from my understanding was how to do the join. Now, I am not sure if this was the best way to accomplish this, but it works.

Thanks again for your advice. Really do appreciate it.

Glad to hear you were able to figure it out.

Regarding your approach, you could define a function in your employees model to do the query just to make the view code a little more clean.

Something like this:




public function getActiveDrivers(){

$query = $this->find()->joinWith('driversActivity', true, 'INNER JOIN')->asArray()->all();

$data = ArrayHelper::map($query, 'id', function($model, $defaultValue){ return $model['first'] .' ' .$model['last']; });


return $data;

}



You could also add the ‘end_time = NULL’ condition as an ON condition in your relation.




public function getDriversActivity(){

return $this->hasOne(DriversActivity::classname(), ['drivers_id' => 'drivers_id'])->andOnCondition('end_time' => NULL);

}



Then you can use this function with an Employee instance. Something like this:




 <?php $employee = new Employee();

        // Driver selection

        echo $form->field($model, 'driver_id')->widget(Select2::classname(), [

            'data' => $employee->activeDrivers,

            'language' => 'en',

            'options' => ['placeholder' => 'Select Driver'],

            'pluginOptions' => [

                'allowClear' => true

            ],

        ]);

    ?>



Haven’t tested it but i think it should work.

I recommend you look some post about working with relations in Yii2 like this (in case you want to work with a GridView) and the official docs here. Those may be helpful to you.

Thanks Gonzalo, yeah definitely will follow your tips to dry-up / clean-up the code.

Best,

psr123

Just wanted to mention that you feeling "super discouraged" is totally normal when you learn new things.

I feel that way too, when I am learning - and it can be the most frustrating experience!!

If you never get that feeling, you are not learning.

What makes us continue, is when we feel "super happy" when suddenly it clicks / works.