Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use relationship value in Laravel Form Facade?

I want to use the values of attributes on a model's relationship with Form::label and Form::text. The Form helper was removed from Laravel, so I use 'Form' => 'Collective\Html\FormFacade' instead.

Here is the relationship in the Order Model:

<?php namespace App\Models;

use Illuminate\Database\Eloquent\SoftDeletes;

class Order extends \Eloquent
{
    use SoftDeletes;

    public function account_number()
    {
        return $this->belongsTo('\App\Models\Account_number', 'product_id', 'id');
    }
}

And here's the Blade template with Form. The text in the account_number <td> will show:

{"id":4,"user_id":52,"account_type":"alipay","account_no":"xxxxxx","account_name":"xxxxxx","phone":"xxxxxx","created_at":"2017-11-15 14:43:51","updated_at":"2017-11-15 14:43:51","deleted_at":null}
{!! Form::model($order, array('files' => true)) !!}
<table border="1">
<tr>
  <td>{!! Form::label('out_trade_no', 'out_trade_no: ') !!}</td>
  <td>{!! Form::text('out_trade_no')!!}</td>
</tr>
<tr>
  <td>{!! Form::label('account_number', 'account_number: ') !!}</td>
  <td>{!! Form::text('account_number')!!}</td>
</tr>
</table>

But I want to show inputs for each account_number attribute separately, not as a JSON string.

I have tried with:

<tr>
  <td>{!! Form::label('account_number.id', 'account_number: ') !!}</td>
  <td>{!! Form::text('account_number.id')!!}</td>
</tr>

or

<tr>
  <td>{!! Form::label('account_number->id', 'account_number: ') !!}</td>
  <td>{!! Form::text('account_number->id')!!}</td>
</tr>

or

<tr>
  <td>{!! Form::label('account_number', 'account_number: ') !!}</td>
  <td>{!! Form::text('account_number["id"]')!!}</td>
</tr>

...but none of these work. They all leave this <td> empty.

like image 701
LF00 Avatar asked Nov 16 '17 02:11

LF00


2 Answers

To create input controls for a model's relationships using the FormBuilder tools provided by Laravel Collective, use the following syntax:

{!! Form::model($order, ...) !!} 
    ...
    {!! Form::label('account_number[id]', 'account number: ') !!}
    {!! Form::text('account_number[id]') !!}
    ...
{!! Form::close() !!}

Note the lack of quotation marks around id. The example in the question contains quotes around the related model's id attribute, which breaks this magic. The code above renders the following input element using the value of the id attribute on the Order model's account_number relationship:

<input name="account_number[id]" type="text" value="4">

The format shown in the input element's name attribute enables PHP to parse the POST data as an array. We can fetch the value submitted to a controller method like in this example:

public function save(Request $request) 
{
    $accountNumber = $request->get('account_number'); 

    echo $accountNumber['id']; // '4'
    ...
}

This feature is important—FormBuilder generates input elements designed to work with PHP's automatic grouping of request data into arrays. If we submit multiple input elements on a form for a model's relationship, Laravel makes it easy to save the result:

public function update(Request $request, $orderId) 
{
    Order::with('account_number')->find($orderId)
        ->fill($request->all())
        ->account_number->fill($request->account_number)
        ->push();
}
like image 66
Cy Rossignol Avatar answered Oct 15 '22 16:10

Cy Rossignol


Let's do something more in-depth understanding.

While generating a text input element, FormBuilder will look for the value in the session for the value in the old input data then it'll look in the model instance if one is set. Otherwise it'll just use empty.
Before it gets the appropriate value, it will transform the key which we specified, such as account_number.id, account_number->id, account_number["id"]. Focus on the transformKey function

protected function transformKey($key)
{
    return str_replace(['.', '[]', '[', ']'], ['_', '', '.', ''], $key);
}

Let's call it with the keys you specified one by one:

  1. account_number.id => account_number_id
  2. account_number->id => account_number->id
  3. account_number["id"] => account_number."id"

Then it will split the key by . return a array $keys. And check if exists nested model with $keys[0], otherwise return the value of master model with transformed key. getFormValue()

No matter it exists nested model or not, it will get the value through data_get() function.

Obviously, the first key and the second you specified doesn't work. The third one account_number."id", it will find the nested model successfully but it won't retrieve the attribute through the way $nestedModel->{"id"}. Use account_number[id] instead, it works well.

<td>{!! Form::text('account_number[id]')!!}</td>
like image 30
Cong Chen Avatar answered Oct 15 '22 14:10

Cong Chen