4

I am building a test Lithium app to learn how it works and I found that the form helper doesn't seem to recognise my data being passed back or any validation errors.

At the moment I'm having to manually pass back my errors and then process them in the view.

QuestionsController::ask

public function ask() {
    if (!empty($this->request->data)) {
        $question = Questions::create($this->request->data);
        if ($question->save()) {
            return $this->redirect(array('Questions::view', 'args'=>$question->id));
        } else {
            $errors = $question->errors();
        }

        if (empty($question)) {
            $question = Questions::create();
        }

        return compact('question', 'errors');
    }
}

views/questions/ask.html.php

<?php
// Assign the protected object to a variable so we can get at it
if(isset($question)){
    $data = $question->data();
}else{
    $data['title'] = '';
    $data['text'] = '';
}
?>

<?=$this->form->create();?>

    <?=$this->form->field('title', array('placeholder'=>'Title your question', 'value'=>$data['title']));?>
    <?php if(isset($errors['title'])){
        echo "<div class='alert alert-error'><a class='close' data-dismiss='alert' href='#'>×</a>";
        foreach($errors['title'] as $e){
            echo $e."<br/>";
        }
        echo "</div>";
    }?>

    <?=$this->form->field('text', array('placeholder'=>'Enter your question (supports Markdown)', 'type'=>'textarea', 'value'=>$data['text']));?>
    <?php if(isset($errors['text'])){
        echo "<div class='alert alert-error'><a class='close' data-dismiss='alert' href='#'>×</a>";
        foreach($errors['text'] as $e){
            echo $e."<br/>";
        }
        echo "</div>";
    }?>
    <p>Text input supports <?php echo $this->html->link('markdown', 'http://en.wikipedia.org/wiki/Markdown');?></p>

    <?=$this->form->submit('Ask', array('class'=>'btn'));?>
<?=$this->form->end();?>

I can see from the lithium\template\helper\Form that the field() method can take a template parameter, which in the example is <li{:wrap}>{:label}{:input}{:error}</li> so there is capacity in the helper for displaying the validation messages.

So how do I organise my data in my controller so that it's passed back to the view in order for the helper to populate my fields and also display errors?

Edit
I should add that the example 'Sphere' app, also uses this method, so is it standard? (ref)

Zombaya
  • 2,230
  • 23
  • 30
David Yell
  • 11,756
  • 13
  • 61
  • 100
  • 2
    Why not create the form from the `$question` object, i.e. `$this->form->create($question)` from your code you will have either a concrete 'question' object or an empty one (and I really don't see the point of the `$data` object in the view file). Passing `$question` to the form helper will enable "automatic" value population for the form fields. – Oerd May 02 '12 at 11:56
  • @Oerd That was because I couldn't figure out how to call `$question->data()['title']` – David Yell May 02 '12 at 11:58
  • @Oerd That's great, please can you form this into an answer and I'll accept it. – David Yell May 02 '12 at 12:01
  • of course, let me type it up :) – Oerd May 02 '12 at 13:28
  • Also note that `$entity->data()` will just delegate to `$entity->to('array')` if no field name is given. So you can go ahead and call `$entity->to('array')` in case you really need the object as array. [$entity->data() documentation](http://goo.gl/XvkW6) PS: sorry to use a shortening service but the `::` in the link breaks the link :( – Oerd May 02 '12 at 14:25
  • @DavidYell `$question->data()['title']` is called `array dereferencing` and isn't supported by php < 5.4. So if you upgrade to 5.4, that style of coding will work. See example #8 in the php array documentation - http://php.net/manual/en/language.types.array.php – rmarscher May 02 '12 at 14:54
  • @DavidYell but, the way you would actually want to do that in lithium is simply `$question->title` :) – rmarscher May 02 '12 at 14:56

1 Answers1

2

TL;DR

the short answer is that you can bind the form to a subclass of Entity, i.e. a Record or Document as shown in Form::create() (link is shortened as :: breaks the link parser).

your code would look like:

<?= $this->form->create($question); ?>
<?= $this->form->field('title'); ?>
<?= $this->form->field('text'); ?>

Long answer:

QuestionsController::ask():

public function ask() {
    $question = Questions::create();
    if (!empty($this->request->data)) {
        if ($question->save($this->request->data)) {
            return $this->redirect(array('Questions::view', 'args'=>$question->id));
        }
    }
    return compact('question');
}

views/questions/ask.html.php:

<?= $this->form->create($question); ?>

    <?= $this->form->field('title', array('placeholder'=>'Title your question'));?>

    <?= $this->form->field('text', array('placeholder'=>'Enter your question (supports Markdown)', 'type'=>'textarea'));?>

    <p>Text input supports <?php echo $this->html->link('markdown', 'http://en.wikipedia.org/wiki/Markdown');?></p>

    <?=$this->form->submit('Ask', array('class'=>'btn'));?>

<?=$this->form->end();?>

Note how the form helper will automatically display errors in case there are any :)

Some useful links into the #li3 philosophy:

1 - http://www.slideshare.net/nateabele/lithium-the-framework-for-people-who-hate-frameworks

2 - Video of roughly the same presentation (w/ a good example on changing form templates)

Oerd
  • 2,256
  • 1
  • 21
  • 35
  • I think the long form of the answer is slightly off. I submitted an edit suggestion. The key to getting values and errors to populate into the form is binding the `$question` entity to it in the `$this->form->create($question)` – rmarscher May 02 '12 at 14:26
  • sorry, typos... (actually more like too quick to copy&paste) Thanks for catching that and editing :) – Oerd May 02 '12 at 14:28