Example site built with Yii2: NFT Viewer

15. Record Create and Update

Now that we have a form using Active Form to submit data, how do we process, validate and save that data into the database?

15.1 Controller Namespace

For the Customers controller, we see the following at the top:

namespace app\controllers;

use Yii;
use app\models\Customers;
use app\models\CustomersSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;

This sets the namespace of this file and includes the Customers model. We also import the Controller methods and an error exception thrown when the record is not found.

We should add one more:

use yii\web\BadRequestHttpException;

This allows us to throw general errors if, for example, there was an error saving the record.

We then define the Customers controller class.

class CustomersController extends Controller
{
    public function behaviors()
    {
        return [
            'verbs' => [
                'class' => VerbFilter::className(),
                'actions' => [
                    'delete' => ['post'],
                ],
            ],
        ];
    }
}

This code extends the Controller class creating the CustomersController where all the CRUD functions reside for the Customers model.

The first method you will see is the behaviors method. In this case, all this method is doing is specifying that the delete action will only accept $_POST data. If you try to delete a record using $_GET data, an error will be thrown.

We will see more use of this behaviors method when implementing user authentication.

15.2 Controller Actions

If you notice the controller created by Gii for managing CRUD operations, you will see an action called actionCreate and an action called actionUpdate. There is actually very little difference between the two.

Both the create and update actions process the form input, run the validation and save the data using Active Record model to the appropriate table. The only real difference is in the create action, we are generating a new model object. In the update action, we are loading an existing model record.

15.2.1 Create New Record

Generating a new model object in the create action for the Customers model is as simple as this:

$model = new Customers();

15.2.2 Updateing Exisitng Record

To update a model record we first must locate the record and load it into the model.

In the Gii generated code, you will see this done as follows:

$model = $this->findModel($id);

The $id parameter passed is the primary key of the record we want to update.

The findModel method actually is not an Active Record method, but refers to a method found at the end of the controller script. This method uses Active Record to load the record.

protected function findModel($id)
{
    if (($model = Customers::findOne($id)) !== null) {
        return $model;
    } else {
        throw new NotFoundHttpException('The requested page does not exist.');
    }
}

The Active Record method used to load the record for the Customers model is:

Customers::findOne($id)

If the record specified by $id is not found, a NotFoundHttpException will be generated displaying a 404 error page to the user.

15.3 Load Submitted Data

At this point, my personal preference is to diverge from the default code generated by Gii.

The first thing we want to do is load the submitted data into the model object, either the new object if creating or the located record if updating.

To do this, we need to access the $_POST data.

15.3.1 Post Data

While the $_POST array is available at this point, we actually use the following statement to access that data:

$post = Yii::$app->request->post();

Here is the difference between the raw $_POST data and the data returned by the Yii::$app->request->post() method:

15.3.1.1 Anatomy of Post Data

When submitting our customers form, here is the actual raw $_POST data received as displayed by print_r() :

Array
(
    [_csrf] => dFNGMXpsUVYcIy9rKQI8ETplH1MWCGEHPCUpBBMKBSYzBw1GHjpiBQ==
    [Customers] => Array
        (
            [customerNumber] => 123
            [companyName] => Company Test
            [contactFirstName] => JB
            [phone] => 4809812885
            [email] => test222@gmail.com
        )

)

Notice that all the submitted data is actually in an array with the key Customers. If we examine the form inputs generated by Active Form, we see why.

Let's look at the input box for Company Name:

<input type="text" id="customers-companyname" class="form-control" name="Customers[companyName]" placeholder="Enter Company Name">

Notice that the field name is Customers[companyName] . If you look back at section 14.2, you will see in the source code for the form that all the input names are in this format. The array key will always be the name of the model generating the Active Form.

Thus, all the form data we want is in the Customers array.

But, what is the [_csrf] all about?

This is a security measure Yii incorporates to combat Cross Set Request Forgery. This security measure prevents malicious users or bots from posting data directly to your controller. The value for the csrf is generated when the Active Form is generated. Then when the form is submitted, the value is checked against what was generated to ensure that the data is coming from the actual Active Form and not from some outside third party.

This is all taken care of behind the scenes and is one of the reasons it is best to use Yii::$app->request->post() to access the post data.

15.3.2 Load Data Into Model

Now that we have loaded the POST data into the variable $post, let's load it into the model. This is accomplished with the following simple statement:

if (!empty($post)) {
	$model->load($post);
}

We first check to see if there actually is any data being submitted to the controller action. Remember, the create and update actions are used to both display the form and process the submitted data. Checking for data in the $post variable allows us to determine that the form was submitted.

We then simply load the data into the model object using the 'load' method.

15.4 Validate Data

Validation of the submitted data is accomplished with the following simple command:

$model->validate()

If the submitted data validates according to the model rules, this method returns True, otherwise it returns False.

Primary Key Auto-increment

We have one problem with our current form and validation. We are requesting Customer Number. However, the customerNumber is the primary key for the customer record and is an auto-increment field. Thus, we should not be passing this value from the form. It is auto generated when saving the record. Therefore, we need to remove it from the validation rules.

15.5 Saving the Submitted Data

Following is the entire code for the create action:

public function actionCreate()
{

    $model = new Customers();
    $post = Yii::$app->request->post();

    if (!empty($post)) {
	    $model->load($post);
	    if ($model->validate()) {
		    if ($model->save()) {
			    Yii::$app->session->setFlash('success', "Customer Data Saved.");
			    return $this->redirect(['view', 'id' => $model->customerNumber]);
		    }
		    else throw new BadRequestHttpException('Error saving customer data.');
	    }
    }

    return $this->render('create', [
        'model' => $model,
    ]);
}

In this code we are generating a new Customers object, we are getting the post data and we are validating that data.

If the submitted data validates, we are calling the save() method for that model. If it saves correctly, we are setting the flash message to indicate the data was saved successfully and then we redirect to the view. We pass $model->customerNumber] to the view controller, which is the auto generated primary key.

If there was an error saving the record, we are throwing an exception.

If there was a validation error, we pass on to the render method for the create view, and if we have $form->errorSummary($model) correctly inserted, we will see a summary of the errors.