Search code examples
relationshiplithium

Lithium: how do I display related data in forms and then save them?


I'm using Lithium with MySQL. I have a Users model that hasOne Contacts. The Contacts model belongsTo Users.

I've listed a very basic version of my code, below.

My questions:

  1. When I edit a user and submit the form, how do I make Users::edit save the Contact data, as well?
  2. Also, how do I display contacts.email in the Users edit view?

models/Users.php

<?php
namespace app\models;

class Users extends \lithium\data\Model {

    public $hasOne = array('Contacts');

    protected $_schema = array(
        'id'   => array('type' => 'integer',
                        'key'  => 'primary'),
        'name' => array('type' => 'varchar')
    );
}
?>

models/Contacts.php

<?php
namespace app\models;

class Contacts extends \lithium\data\Model {

    public $belongsTo = array('Users');

    protected $_meta = array(
        'key'   => 'user_id',
    );

    protected $_schema = array(
        'user_id' => array('type' => 'integer',
                           'key'  => 'primary'),
        'email'   => array('type' => 'string')
    );
}
?>

controllers/UsersController.php

<?php
namespace app\controllers;

use app\models\Users;

class UsersController extends \lithium\action\Controller {
    public function edit() {
        $user = Users::find('first', array(
                'conditions' => array('id' => $this->request->id),
                'with'       => array('Contacts')
            )
        );

        if (!empty($this->request->data)) {
            if ($user->save($this->request->data)) {
                //flash success message goes here
                return $this->redirect(array('Users::view', 'args' => array($user->id)));
            } else {
                //flash failure message goes here
            }
        }
        return compact('user');
    }
}
?>

views/users/edit.html.php

<?php $this->title('Editing User'); ?>
<h2>Editing User</h2>
<?= $this->form->create($user); ?>
    <?= $this->form->field('name'); ?>
    <?= $this->form->field('email', array('type' => 'email')); ?>
<?= $this->form->end(); ?>

Solution

  • Not many people know this but with lithium you can bind a form to multiple objects.

    In your controller, return both a user and a contact object. Then in your form:

    <?= $this->form->create(compact('user', 'contact')); ?>
    

    You then render a field form a specific object like this:

    <?= $this->form->field('user.name'); ?>
    <?= $this->form->field('contact.email'); ?>
    

    When the user submits the form, the data for both objects will be stored as:

    $this->request->data['user'];
    $this->request->data['contact'];
    

    You can use this information to update the database as you would normally. If you only want to save the information if data from both objects are valid, you can call validate like this:

    $user = Users::create($this->request->data['user']);
    if($user->validates()) {
        $userValid = true;
    }
    
    $contact = Contacts::create($this->request->data['contact']);
    if($contact->validates()) {
        $contactValid = true;
    }
    
    if($userValid && $userValid){
        // save both objects
    }
    

    Hope that helps :)