Handling 3 Related Models (A->B->C ), Including Some Crud

Hello, dear Community. Uzbekistan here :lol:

This is my first post here, so this might mean that I was really looking for the answer everywhere and couldn’t find it, considering that I do not like to bother people for nothing :D

My question consists of two parts - general and specific.

What I have:

1st table: application:

  • id

  • first_name

  • last_name

… 30+ fields for private information

2nd table: app_company:

  • id

  • company_name

  • applicant_id

  • region_id

… 30+ fielfs for information about company

3rd table: app_region

  • id

  • region_name (12 regions are stored here)

My relations:

Application model


return array(

    'company_info' => array(self::HAS_ONE, 'AppCompany', 'applicant_id'),

    );

AppCompany model:


return array(

    'application' => array(self::BELONGS_TO, 'Application', 'applicant_id'),    

    'region' => array(self::BELONGS_TO, 'AppRegion', 'region_id',)    

    );

AppRegion model (by the way, do I really need this model?):


return array(

    'company' => array(self::HAS_MANY, 'AppCompany', 'region_id',) 

    );

Here is my actionCreate for the ApplicationContoller (just for reference):


public function actionCreate()

{

    $application = new Application;

    $company = new AppCompany;


    // Uncomment the following line if AJAX validation is needed

    // $this->performAjaxValidation($model);


    if(isset($_POST['Application'], $_POST['AppCompany']))

    {

        $application->attributes=$_POST['Application'];

        $company->attributes=$_POST['AppCompany'];


        $valid=$application->validate();

        $valid=$company->validate() && $valid;


        if($valid)  

        {  

            if($application->save(false))

            {

                $company->applicant_id = $application->id;

                $company->save(false);

                $this->redirect(array('view','id'=>$application->id));

            }

        }

    }

    $this->render('create',array(

        'application'=>$application,

        'company'=>$company,

    ));

}

My form:


<?php echo $form->errorSummary(array($application, $company)); ?>

<!-- *** -->

<div class="row">

    <?php echo $form->labelEx($company,'company_name'); ?>

    <?php echo $form->textField($company,'company_name',array('size'=>60,'maxlength'=>75)); ?>

    <?php echo $form->error($company,'company_name'); ?>

</div>

<div class="row">

    <?php echo $form->labelEx($application,'last_name'); ?>

    <?php echo $form->textField($application,'last_name',array('size'=>50,'maxlength'=>50)); ?>

    <?php echo $form->error($application,'last_name'); ?>

</div>

<div class="row">

    <?php echo $form->labelEx($company,'company_region_id'); ?>

    <?php echo $form->dropDownList($company,'company_region_id', CHtml::listData(AppRegion::model()->findAll(), 'id', 'region_title_ru'), array('empty'=>'Выберите область')); ?> // PAY ATTENTION HERE

    <?php echo $form->error($company,'company_region_id'); ?>

</div>    

So, while applying a new application (actionCreate), I can populate both some personal information and company information using single form. Moreover, I can choose a region title using dropdown and save it into AppCompany “region_id” cell (for example, ‘7’ if I’ve chosen ‘Tashkent region’).

After that I, want to show the result in a view file:


?php $this->widget('bootstrap.widgets.TbDetailView', array(

'data'=>$model,

'type'=>'striped bordered condensed',

'attributes'=>array(

    'id',

    //

    'company_info.company_name',

    'company_info.date_of_foundation',

    'company_info.company_region_id' // HOW TO SHOW NOT ID, BUT THE TITLE?

    'last_name',

    ... 60+ fields

'first_name'

So, the 1st question (SPECIFIC):

Is it possible to show the name of the region, which is actually located in the THIRD table, while the direct relations between THIRD and FIRST table can’t be established? (OR CAN?) How could I realize this feature?

2nd question (GENERAL):

Is it "ok way" to build the database in a such way in this case?

I think that the most evident way is to move the region_id into application table. So, there would be a direct relation between 1st and 3d tables and it would be much easier to perform what I need, especially considering that this application form would be used as one - there is no need to use company and applicant’s info separately. I’m doing that only because I will have a huge amount of information with more related tables with 200+ fields. But I really think it’s a good practice to keep all company and personal info in separate tables. At least, it’s a kind of logical.

So, sorry for bothering and thank you all in advance!

Hi and welcome to the forum.

  1. Yes, see this.

  2. Yes.

First of all, thank you for reply.

I have met this doc before, but couldn’t implement it in my case and decided that the things that are described there - for some differend situation. As for as I inderstood, it was a mistale? :rolleyes:

Ok, then do I need to do the next steps?

Applicant HAS_ONE company only and a company BELONGS_TO one region. So, what i need is HAS_ONE through?

Then besides my relations I need to add this to Applcation model?:


'region'=>array(

                self::HAS_ONE,'AppRegion',array('region_id'=>'id'),

                    'through'=>'Company'

            ),

		);

If yes, then how I can get


'company_info.region_name[NAME HERE]'

instead of


'company_info.region_id'

?

Actually I think the ‘region’ relation should be a MANY_MANY.

As for getting region name information? Load the model using the ->with() operator.


Application::model()->with('company_info, region')->findxxx();


model.company_info.region.name //<== region Name

Thank you. Will test it now. Could you please look at my actionView?


public function actionView($id)

	{

		$application = $this->loadModel($id);

        $company = AppCompany::model()->find('applicant_id=:applicant_id', array(':applicant_id'=> $id));

        

        $this->render('view',array(

			'application'=>$application,

            'company'=>$company,

		));

	}

As for as I understood from your words, I don’t need $company:AppCompany::model… anymore because this:


Application::model()->with('company_info, region')->findxxx();

will replace it with the same functionality?

And what do you mean under "findxxx()"?

Thank you

findxxx() means:

->find();

->findAll();

->etc…

I have not tried it, but if region is MANY_MANY then I think it is correct AppCompany::… is not needed.