CActiveForm on layouts column1.php

Goodmorning to everyone,

i’m new to yii and i must say it i’m pretty noob with it XD

so today i was trying to create a form with CActiveForm and i first put it in the layout/column1.php but it doesn’t work, after a while i tried to put it on site/inddex.php and it worked… the problem is that i need that damn form in the column1.php cause i’m building a 3 cols website where the left and right cols are always the same… so i thought to put the form there but i get this error :




Fatal error: Call to a member function isAttributeRequired() on a non-object in /Applications/MAMP/htdocs/discodisco/framework/web/helpers/CHtml.php on line 1179



so my question is how do i get cactiveform working on column1.php or how do i get a 3cols template without repeat on each page the same code (that will cause a big headache if in the future i’ll need to modify something)

i’'ll appreciate the help that you’ll give to me.

Every controller has a specific layout attribute.


<?php


class SiteController extends Controller

{

	public $layout='column1';



If you want to change layout, change it!

Yii rocks!

In the layout you don’t have access to the data passed in the call to render(). But you still are in the controller scope.

You may want to have a closer look at portlets. Here’s the API reference.

Note that the portlet won’t examine posted data until instantiated in the view. I think a more intuitive structure could be used for portlets containing model data that needs to be saved. My consideration here is with regard to execution flow.

1. Either a "container" component that will be used for validating and saving posted data then called again from the view code for instantiating an aggregated widget.

2. Or a portlet extended with a data handling method that will be called from the controller action just after the instance is created (renderContent() will of course be called from the view/layout).

To me this seems more MVC conformant and may also be advantageous in an ajax call scenario. But on the other hand the controller will become dependent on widgets used in the view. (It would not in the container approach using a widget list injected from config).

Just thinking loud here.

/Tommy

thanks that is exactly what i needed ^^ now i’ll try it :D

I just tested the extended CPortlet with a CActiveForm in it’s view. I used content from protected/<controller>/_form.php. I also copied the loadModel() and performAjaxValidation() methods from the controller.

For simplicity (and ajax validation ability) I chose to instantiate the portlet in the controller action (e.g actionUpdate), saving the result into a public member variable.

SomeController.php




public somePortletMarkup;

...

$this->somePortletMarkup = $this->widget('application.components.portlets.SomePortlet', array('id'=>$id), true);




Instead of passing in the record id, _GET[] can be used in the portlet.

The variable can be echoed from column1.php.

SomePortlet.php




Yii::import('zii.widgets.CPortlet');


// NOTE: Very experimental!!!


class SomePortlet extends CPortlet

{

  public $title='Some title';

  public $modelClass = 'SomeModel';

  public $model;

  public $id;

  public $view = '_form';

  public $formId = 'some-form';


  public function init()

  {

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

    $this->performAjaxValidation($model);


    if(isset($_POST[$this->modelClass]))

    {

      $model->attributes = $_POST[$this->modelClass];

      if ($model->validate())

      {

        if ($model->save())

          $this->view = '_saved';

      }

    }

    $this->model = $model;

    parent::init();

  }


  protected function renderContent()

  {

    $this->render($this->view, array('model'=>$this->model, 'formId'=>$this->formId));

  }


  public function loadModel($id)

  {

    $model=CActiveRecord::model($this->modelClass)->findByPk((int)$id);

    if($model===null)

      throw new CHttpException(404,'The requested record does not exist.');

    return $model;

  }


  protected function performAjaxValidation($model)

  {

    if(isset($_POST['ajax']) && $_POST['ajax']===$this->formId)

    {

      echo CActiveForm::validate($model);

      Yii::app()->end();

    }

  }

}



The views goes to the <portletlocation>/views subdirectory

Edit:

  • Pass formId to the view.

  • Cleaned up the code a bit.

/Tommy

I am using a CClipWidget in my controllers before action - you could probably use that with a CPortlet too.

This is a wiki article which seems relevant:

http://www.yiiframework.com/wiki/127/dynamic-sidebar-using-cclipwidget/

Thanks Jac, will have a look. I didn’t search for existing code because I just had to dig into this myself. Really needed some practice.

One thing that puzzles me, though, is that the validation result is echoed from inside the portlet, presumably because the captured data will be flushed on Yii::app()->end(). Or perhaps the capture isn’t started until render() is called.

Edit: CPortlet also works well (without third parameter) enclosed in $this->beginClip(‘someClip’)/$this->endClip(), so no need to declare a public variable in controller(s). I think a CWidget is appropriate, CPortlet inherits from the former, mainly adding some decoration support.

/Tommy