Basic View Edits in Yii

November 12, 2009
The Yii Book If you like my writing on the Yii framework, you'll love "The Yii Book"!
This entry is part 7 of 8 in the series Learning the Yii Framework

This is the seventh post in my series on Yii, my favorite PHP framework. In this post, I walk through some basic View edits you’ll make to the code auto-generated by the Yii framework. For some of the code, I’ll be using the employees-departments example I’ve been developing throughout these posts. You may want to reread earlier posts to get a handle on those.

(Note: In October 2010, I’ve updated this entire series to reflect changes in Yii since this series was written, and to take into account feedback provided through the comments. Some outdated material will be crossed out, but left in to reflect how things have changed since the series was begun in June 2009.)

When you use the command-line and Gii tools to create a new Web application, the Yii framework generates a series of files and folders for you, from configuration files to controllers to Views. The Views are a series of PHP scripts, organized by folder. The first one you’ll want to edit is protected/views/layouts/main.php (where protected is the root application directory). This is the template for every page within the application. You’ll likely want to change most of the file to create your own look, but I want to highlight a few key points.

To start in the HEAD, you’ll see that external files are linked using

<link rel="stylesheet" type="text/css" href="<?php echo Yii::app()->request->baseUrl; ?>/css/main.css" />

Whenever you see Yii::app(), that refers to the Web application as a whole. You can access information about the user viewing a page, about the current or previously-viewed page, etc., there. Yii::app()->request specifically references the current page being accessed (or requested). The ->baseUrl part refers to the root URL for the application, like http://www.example.com. You should use Yii::app()->request->baseUrl for references to external files—CSS, JavaScript, images, and so forth—as the relative path to them can become muddled with the changed Yii URLs (like www.example.com/index.php/site/login).

Next, you’ll see the page’s title set dynamically:

<title><?php echo CHtml::encode($this->pageTitle); ?></title>

By default, the pageTitle value will be the application’s name (defined in the config file) plus something about the current page. Later on, I’ll show you how to change this. The CHtml::encode() method is just used to protect against Cross-Site Scripting (XSS) attacks. You’ll see it used liberally (and appropriately) in the View files.

You’ll also see in the main layout file:

<div id="logo"><?php echo CHtml::encode(Yii::app()->name); ?></div>

Yii::app()->name is the name of the Web application, as established in the config/main.php file. You may or may not want to use it in your Views, but that’s where the value comes from.

Next up, the default layout uses a widget:

<?php $this->widget('zii.widgets.CMenu',array(...

Widgets are a way to include a nugget of PHP code in a View using an external file. They’re best for things that aren’t tied to specific Views and that you may want to drop in anywhere. Plus it allows you to further separate out chunks of code for easier management and portability. The above code says that the CMenu widget should be dropped in here. That widget is part of the Zii extension (to the Yii framework). For more on widgets, see the Yii documentation.


Edit for Yii 1.1: Yii 1.1 took a bunch of the best extensions and placed them in their own namespace, zii. Whereas earlier versions of Yii created a MainMenu component, Yii 1.1 uses the CMenu widget. The default layout also makes use of the CBreadcrumbs widget, include in the Zii extensions.


Finally, the layout script has this line, which is the most important:

<?php echo $content; ?>

Now you won’t find a reference to the $content variable anywhere in your code and do note that it’s just $content, not $this->content or $model->content. This is where the page-specific content gets inserted into the layout. All the other HTML and PHP is the template for the entire site; the value of $content is what makes pageX different from pageY. So where does it come from?

Tip: The exact structure of the URLs will depend upon whether you’ve enabled urlManager or not and, if so, how it’s configured. See the post on configuring Yii for more.

If you’re viewing a page like www.example.com/index.php/employee/view/id/1, which is intended to show the employee with an ID of 1, the actionView() method of the EmployeeController class is called. That method loads the Employee Model with a primary key of 1, then renders the view View, passing along the Model in the process. That code is (this is from protected/controllers/EmployeeController.php):

public function actionView($id)
{
    $this->render('view',array(
        'model'=>$this->loadModel($id),
    ));
}

The loadModel() method of this Controller class does the actual Model retrieval, and I won’t worry about that right now, but the $this->render() part says to render the View to be named, in this case, view. That means that Yii will execute the PHP script protected/views/employee/view.php. That script uses a $model variable (passed in the above code) to display information about the employee within some context (specifically, view.php script uses zii.widgets.CDetailView to list the details) . The result of this executed View script is, behind the scenes, assigned to $content, and therefore dropped into the appropriate place in the layout. That’s how the system works.

One last thing about the layout scripts is that you can easily have different layouts for different sections of the application. To do so, in the Controller method, change the layout value before rendering the view. Provide the name of the layout file, minus the extension. So this line says to use protected/views/layouts/home.php for the index action:

class SiteController extends CController
{...
    public function actionIndex()
    {...
        $this->layout = 'home';

In fact, as of Y 1.1.?, the framework creates both one column and two column layouts, each of which use the main.php template file. The Controller then indicates which layout to use for the entire Controller:

class EmployeeController extends Controller
{
    public $layout='//layouts/column2';

Note: $this->layout within a class method is equivalent to just public $layout outside of any method (as in the above code). The first example changes the layout for a specific action; the second for the Controller as a whole.

Moving on to the View scripts, one of the first things I normally do is change the basic formatting to whatever you’re looking for. For example, the default appearance of a show View is a table of information about the Model: labels and values. Normally you don’t want to show all of the Model data, and you’ll display it in DIVs or paragraphs. These are simple edits. As the above code shows, the Model information will be passed under the name model, so you can use $model->whatever to access the values of the different fields.


Edits for Yii 1.1: There are a number of View file changes, although none that are dramatic: What was previously the list (list.php) View file is now index (index.php). The index page makes use of a CListView widget to list the records. The admin page makes use of a CGridView widget to display all the records. The show file (show.php) is now called view (view.php). It makes use of the CDetailView widget to show the information for a specific record. Multiple View files can make use of the _view.php script, which is a template for showing an individual record. Every view file identifies breadcrumb information at the top, which will tie into the CBreadcrumbs widget referenced in the main layout file.


Tip: If you log into the site (using admin/admin if you haven’t changed the login system) and then go to www.example.com/index.php/department/, you can already create a few department records to make better sense of what I write below.

The index and admin Views both show multiple Model records, using Zii widgets. index.php uses the CListView widget, using the _view.php script as the template. If you edit either _view.php or how CListView is configured (in index.php), you’ll change the listings. The admin page, accessed by clicking on “Manage XXX”, uses the CGridView widget. This widget creates a table of data, with links to view, edit, or delete specific records. The table can be paginated and searched, using Ajax. It’s really sharp and a great demonstration of how much Yii will do for you. This figure shows the departments listing, without having made any changes to what Yii created:

Department Admin Page

Department Admin Page

The create and update Views have some page header stuff, then include the form View, using this code:

<?php echo $this->renderPartial('_form', array('model'=>$model)); ?>

That code renders a partial View in that place, passing along the Model information to _form.php.

Looking at the generated code, the Department form is fine, as it just provides an input for the department’s name. For the Employee Model, however, there’s some customization you’ll need to do there. You can check it out by heading to www.example.com/index.php/employee/create/.

By default, the forms generated by Gii includes elements for every Model attribute, save for the primary key. But some fields, like the dates created or modified, may be automatically populated with timestamps, and therefore aren’t inputted by the users. And, with related Models, like Employee and Department, you’ll end up needing a drop-down menu in the one to select a value from the other, like choosing what department an employee is in. The Yii-generated code won’t do this for you; the form will just have a text field created by this code:

<?php echo $form->textField($model,'departmentId'); ?>

The $form variable is an object of CActiveForm type. The textField() method creates a text input. The first argument says that the input should be for the $model object (which is the current Model instance, coming from the Controller). The second argument identifies the property in the Model that this element is for.

To create a drop-down associated with another Model, you’d replace that code with:

<?php echo $form->dropDownList($model,'departmentId', CHtml::listData(Department::model()->findAll(), 'id', 'name')); ?>

First, the dropDownList() method of the $form object creates a drop-down list. You need to tie it to the form-associated Model, so the first argument is just $model, the same variable passed to this View when it’s rendered. The second argument is the name of the form field/Model field: here, the departmentId field in the Employee Model. Next, you need to provide the method with the list of values to use for the drop-down menu, which is achieved by calling CHtml::listData(). That method returns a list of values that are usable in drop down menus. Its data source should be the list of Departments. To retrieve those, use Yii’s approach for retrieving all the records in a Model: ModelName::model()->findAll(). So to fetch every department, use Department::model()->findAll(). The final two arguments (to the CHtml::listData() method) are the fields to use for the drop-down menu’s value and displayed text. Those should be id (the department’s ID) and name.

Another thing you’ll want to do in all your View files is remove or edit the links to the different admin features.

Finally, you may decide you want to change the page’s title. To do that, use code like:

<?php $this->pageTitle = $model->something; ?>

Note that you’re assigning a value to $this->pageTitle here, not $model->pageTitle, but you’ll likely use the contents of $model, like a title or name field, as the page title value. You can also still add in the application name, if you want, by concatenating in Yii::app()->name.

Whew! So that’s my whirlwind tour of basic View edits you’ll want to make to a fresh Yii application. In my next post, I’ll discuss basic Controller edits, of which there are surprisingly few, I find. As always, thanks for reading what I have to say and do let me know if you have any questions or comments.