DEV Community

Serhii
Serhii

Posted on

How to display two fields with same name in BackPack For Laravel

Table Of Contents

Have you ever heard anything about BackPack for Laravel?
It's Laravel Admin Panel. Fast & easy to integrate. But then could be some issues... For example:

Real case

You have a two tables (order and person):

Schema::create('orders', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->unsignedBigInteger('primary_person');
    $table->unsignedBigInteger('secondary_person');
});

Schema::create('persons', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('first_name', 255);
    $table->string('last_name', 255);
});

Each order has relation to primary and secondary persons by ID:

class Order extends Model
{
    use CrudTrait;

    protected $table = 'orders';
    protected $fillable = [
        'primary_person',
        'secondary_person',
    ];

    public function primaryPerson()
    {
        return $this->hasOne(Person::class, 'id', 'primary_person');
    }

    public function secondaryPerson()
    {
        return $this->hasOne(Person::class, 'id', 'secondary_person');
    }
}

What need to do?

BackPackForLaravel provides possibility to quickly generate CRUD pages. In our case we need to edit order page.
So, let's create text inputs for:

  • First name of Primary person
  • Last name of Primary person
  • First name of Secondary person
  • Last name of Secondary person

Bad solution (wrong way)

According to documentation in OrderCrudController.php need to add:

$this->crud->addFields([
    [
       'label' => 'First Name of Primary person',
       'type' => 'text',
       'name' => 'first_name',
       'entity' => 'primaryPerson',
       'model' => 'App\Models\Person',
    ],
    [
        'label' => 'Last Name of Primary person',
        'type' => 'text',
        'name' => 'last_name',
        'entity' => 'primaryPerson',
        'model' => 'App\Models\Person',
    ],
    [
        'label' => 'First Name of Secondary person',
        'type' => 'text',
        'name' => 'first_name',
        'entity' => 'secondaryPerson',
        'model' => 'App\Models\Person',
    ],
    [
        'label' => 'Last Name of Secondary person',
        'type' => 'text',
        'name' => 'last_name',
        'entity' => 'secondaryPerson',
        'model' => 'App\Models\Person',
    ]
]);

Wrong result

Attribute name in controller it's also name in text input.
It have to be uniq!
So, we get only latest two inputs:
Two fields in form

Let's think how to cheat.

Better solution (Accessors & Mutators)

Go to Laravel's documentation and read about accessors and mutators.

We could define new names of same field.
For example,

Primary person fields become:
primary_first_name
primary_last_name

Secondary person fields become:
secondary_first_name
secondary_last_name

Let's create two models. PrimaryPerson:

namespace App\Models;

class PrimaryPerson extends Person
{
    public function getPrimaryFirstNameAttribute()
    {
        return $this->attributes['first_name'];
    }

    public function setPrimaryFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = $value;
    }

    public function getPrimaryLastNameAttribute()
    {
        return $this->attributes['last_name'];
    }

    public function setPrimaryLastNameAttribute($value)
    {
        $this->attributes['last_name'] = $value;
    }
}

and SecondaryPerson:

namespace App\Models;

class SecondaryPerson extends Person
{
    public function getSecondaryFirstNameAttribute()
    {
        return $this->attributes['first_name'];
    }

    public function setSecondaryFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = $value;
    }

    public function getSecondaryLastNameAttribute()
    {
        return $this->attributes['last_name'];
    }

    public function setSecondaryLastNameAttribute($value)
    {
        $this->attributes['last_name'] = $value;
    }
}

New we can change Fields in OrderCrudController.php

$this->crud->addFields([
    [
       'label' => 'First Name of Primary person',
       'type' => 'text',
       'name' => 'primary_first_name',
       'entity' => 'primaryPerson',
    ],
    [
        'label' => 'Last Name of Primary person',
        'type' => 'text',
        'name' => 'primary_last_name',
        'entity' => 'primaryPerson',
    ],
    [
        'label' => 'First Name of Secondary person',
        'type' => 'text',
        'name' => 'secondary_first_name',
        'entity' => 'secondaryPerson',
    ],
    [
        'label' => 'Last Name of Secondary person',
        'type' => 'text',
        'name' => 'secondary_last_name',
        'entity' => 'secondaryPerson',
    ]
]);

Do not forget about Storing!

Fetching works good, but we broke store action. Need to implement custom one.

public function update(UpdateRequest $request)
{
    $this->crud->hasAccessOrFail('update');
    $this->crud->setOperation('update');

    if (is_null($request)) {
        $request = \Request::instance();
    }

    /* Storing main object
    $item = $this->crud->update($request->get($this->crud->model->getKeyName()),
        $request->only([
            'primary_person',
            'secondary_person',
        ])
    );
    $this->data['entry'] = $this->crud->entry = $item;
    */

    $this->updatePrimaryPerson($request);
    $this->updateSecondaryPerson($request);

    \Alert::success(trans('backpack::crud.update_success'))->flash();
    $this->setSaveAction();
    return $this->performSaveAction($item->getKey());
}

private function updatePrimaryPerson(UpdateRequest $request)
{
    $person = PrimaryPerson::find($this->crud->entry->primary_person);
    $person->primary_first_name = $request->get('primary_first_name');
    $person->primary_last_name = $request->get('primary_last_name');
    $person->save();
}

private function updateSecondaryPerson(UpdateRequest $request)
{
    $person = SecondaryPerson::find($this->crud->entry->secondary_person);
    $person->secondary_first_name = $request->get('secondary_first_name');
    $person->secondary_last_name = $request->get('secondary_last_name');
    $person->save();
}

Works!

Four fields in admin panel

Good luck folk!

Top comments (1)

Collapse
 
polfo profile image
Polfo

I go for this method, which is clean.
backpackforlaravel.com/docs/4.1/cr...