Jump to content
Larry Ullman's Book Forums

Still Trying To Understand Yii


another_noob
 Share

Recommended Posts

I hunkered down this winter to get a better grasp on OOP, MVC, frameworks, and then Yii. Now I am trying to apply all this new knowledge, or my lack of... which is about to become very obvious.

 

I have a very simple application design concept that works flawlessly in procedural PHP. A simplified version looks like this:

 

A database table of "client" and a table of "project". A one (client) to many (project) relationship with a "client_id" foreign key relating to "id" in client table. Couldn't be simpler.

 

I use composer to spin up a Yii 2 skeleton, change the config/db file to my database settings.

 

Now with Gii I spin up my models and CRUD. Gii even sets up the table relationships...magic I say!

 

Here is what I don't get about all this:

 

Gii spins up the necessary CRUD forms which is great, but the project form has no knowledge of the client model. If I go to "localhost/myapp/web/index.php?r=project/create" I get the project entry form, but the first form entry is client ID. That is fine if I know the "id" from the client model. I don't understand how I link a project model to a client model dynamically in code. If Gii knew about the one->many relationship of these two tables, why not give me the model link in code to allow adding a project to the client with some PK to FK linked forms.

 

I am baffled by this. I don't see how to make this happen with the Gii generated code. This was incredibly simple in my procedural projects. I must have missed a tidbit of knowledge somewhere that explains how I do this simple thing with the Yii framework.

 

I don't expet Yii or Gii to read my mind and do what I am thinking, I know I need to write code to get what I want and be gracious to Gii for all of the time saving boiler plate code.

 

If anyone can enlighten me a tad or point me to the sentence I missed that makes this concept clear, I would greatly apprciate it.

 

Oh, I am using Yii 2, but it shouldn't matter for this question.

 

 

Link to comment
Share on other sites

So what are you saying? THat the client is field should be pre populated wih existing clients?

 

If so just pass the form a drop down box instead of text field and pass it the appropriate model. It will be something like this

 

$model = Client::find()->all();

$dropDown = ArrayHelper::map($model, 'client_name', 'client_id');

 

Pass $dropDown to the form now. Job done

Link to comment
Share on other sites

Thank you for your time Jonathon. I see what you are getting at. I don't think I was very clear in what my confusion is. I will explain this further.

 

Same scenario, client->one-to-many->project. My client/index route renders a gridview of client names. I can click on the view icon to open a client/view&id=3 (for example). This will render my view that codes like this:

<?php
use yii\helpers\Html;
use yii\widgets\DetailView;

/* @var $this yii\web\View */
/* @var $model app\models\Client */

$this->title = $model->client;
$this->params['breadcrumbs'][] = ['label' => 'Clients', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="client-view">

    <h3>Client Projects</h3>
    
    <ul class="list-project">
        <?php foreach($model->projects as $project):?>
            <a href="index.php?r=project/view&id=<?= $project['id'] ?>" class="">
                <li class="project_list_item">
                    <?= $project['proj_name']?>
                </li>
            </a>    
        <?php endforeach;?>    
    </ul>
    
    <?= Html::a('Add A New Project', ['project/create'], ['class' => 'btn btn-success']) ?>
    
</div>   

This view gives me a list of hyperlinks of the current projects for the client with the id of 3. I can click a project hyperlink and the project/view&id=1 route will render a view showing all the pertinent details about the project. With the data I manually entered into my database tables all of this works the way I want it to.

 

My confusion is this: in that view I have a button to create a new project for the client:

<?= Html::a('Add A New Project', ['project/create'], ['class' => 'btn btn-success']) ?>

This line of code renders the _form to enter a new project via the project/create route. I don't know how to get the client model PK to have available to insert into the project table thus cementing the relationship of this new project to the client with the id of 3, in this example.

 

The Gii generated views/project/form:

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model app\models\Project */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="project-form">

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

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

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

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

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

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

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

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

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

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

</div>

From a user's perspective it has to as easy as this:

 

The User sees a list of clients,

         

     Compuware

     U of M Baseball

     U of M Football

     U of M Hockey

     U of M School of Social Study

     Whirlpool

 

User clicks a client choice (view icon) and is then presented the list of current projects for that client and and the end of the list has a button to add a new project to the list that is automatically linked to the client, without the use needing to remember anything about the client that they clicked. The dropdown idea would work, but my users will be able to add a football stadium project to Whirlpool instead of U of M Football as they intended.

 

Sometimes it takes me a bit to explain the problem I am having in a coherent fashion because I don't completely understand what I am trying to say. I think this is a more lucid explanation than last night's ramblings.

Link to comment
Share on other sites

Okay, so I got this working. Here is how I did it, probably is an easier way, but I don't know one.

 

To get the PK of the client, I send it from the clientController's actionView function when a user clicks the view icon on the index page:

 public function actionView($id)
 {
    return $this->render('view', [
    'model' => $this->findModel($id),
    'client_id' => $id,
   ]);
 }

In My client/view script that renders the list of projects belonging to the client, I now have the $client_id variable available to send as an argument to the projectController's actionCreate function. I had to use 'yii\helpers\url' to take advantage of "url creation" in my button code. So now the client/view script that renders the list of projects belonging to a client looks like this:

<?php
use yii\helpers\Html;
use yii\helpers\Url;
use yii\widgets\DetailView;

/* @var $this yii\web\View */
/* @var $model app\models\Client */

$this->title = $model->client;
$this->params['breadcrumbs'][] = ['label' => 'Clients', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="client-view">

  <h1><?= Html::encode($this->title) ?></h1>
	
  <h3>Client Projects</h3>
	
    <ul>
	<?php foreach($model->projects as $project):?>
	    <a href="index.php?r=project/view&id=<?= $project['id'] ?>" >
		<li>
		    <?= $project['proj_name']?>
		</li>
	    </a><br />	
	<?php endforeach;?>	
    </ul>
	
 <?= Html::a('Add A New Project', Url::to(['project/create', 'client_id' => $client_id]), ['class' => 'btn btn-success']) ?>

</div>

Now when the "Add A New Project" button is clicked, the $client_id is passed to the projectController's actionCreate function which I have changed to accept an argument of $client_id.

 

So now the projectController's actionCreate function looks like this:

public function actionCreate($client_id)
    {
        $model = new Project();
	
        if ($model->load(Yii::$app->request->post()) && $model->save()):
            return $this->redirect(['view', 'id' => $model->id]);
        else:
            return $this->render('create', [
                'model' => $model,
		'client_id' => $client_id,
	    ]);
        endif;
    }

So now I have passed the $model and $client_id variables to the views/project/create script that now looks like this:

<?php

use yii\helpers\Html;


/* @var $this yii\web\View */
/* @var $model app\models\Project */

$this->title = 'Create New Project';

$this->params['breadcrumbs'][] = ['label' => 'Projects', 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
?>
<div class="project-create">

    <h1><?= Html::encode($this->title) ?></h1>

    <?= $this->render('_form', [
        'model' => $model,
	'client_id' => $client_id,
    ]) ?>

</div>

Which basically just renders the html form script that now has access to the $model and $client_id variables. The _form view script now looks like this:

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $this yii\web\View */
/* @var $model app\models\Project */
/* @var $form yii\widgets\ActiveForm */

?> 
<div class="project-form">

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

    <?= $form->field($model, 'client_id')->input('hidden', ['value' => $client_id])?>

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

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

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

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

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

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

    <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 went with a hidden form field to get the $client_id back to the controller->model for insertion into the database. I had to use trial and error on the hidden form field syntax because I couldn't find a single example anywhere for that.

 

It works as expected, and now I can do the same for client contacts and for any specific contacts associated with a specific project.

 

If anybody has a better yii 2 implementation of this idea, I am all eyes. There must be a simple one line of code solution at the model level, but I can't figure it out with my limited experience with Yii2/MVC/OOP.

Link to comment
Share on other sites

  • 3 weeks later...

I think this is pretty decent as is. A minor quibble: your Client controller actionView() method doesn't need to pass the client_id to the view, as the view will already have it as $model->id. Again, this is minor. 

Link to comment
Share on other sites

 Share

×
×
  • Create New...