the repeater field is one of the essential fields in laravel backpack that eases alot of stuff for both the backend and front end, usually it can work easily and out of the box for one to many relationships but how can we use it for many to many relationships with pivot data and with Ajax loading.
in this example we will consider the following, we have model Project and Model Assets and let’s add the cost and number of items between their relationship and since we can have many different Assets in our database we will load them as Ajax.
so let’s prepare everything.
first we will have our Project and Asset Model and the relationship between them:
//Project Model
public function assets()
{
return $this->belongsToMany(Asset::class)->withPivot(['quantity', 'cost']);
}
//Asset Model
public function projects()
{
return $this->belongsToMany(Project::class)->withPivot(['quantity', 'cost']);
}
and Now we can add the repeater field in our Project CRUD Controller:
//Project CRUD Controller
$this->crud->addFields([
[
'name'=>'assets_total',
'type'=>'repeatable',
'label'=>'Assets Included',
'init_rows' => 0,
'fields' => [
[
'name' => 'id',
'type' => 'hidden',
],
[
'name' => 'asset',
'type' => "relationship",
'label' => 'Asset Name',
'ajax' => true,
'multiple' => false,
'entity' => 'assets',
'model' => Asset::class,
'attribute'=> 'name',
'wrapper' => ['class' => 'form-group col-md-6'],
],
[
'name' => 'quantity',
'type' => 'number',
'label' => 'Quantity',
'wrapper' => ['class' => 'form-group col-md-3'],
],
[
'name' => 'cost',
'type' => 'number',
'label' => 'Cost',
'wrapper' => ['class' => 'form-group col-md-3'],
],
]
]]);
now we prepare our fetch Function For the assets in our Project Crud Controller
public function fetchAssets()
{
return $this->fetch([
'model' => Asset::class,
'query' => function($model) {
$search = request()->input('q') ?? false;
if ($search) {
return $model->where('condition', true);
}else{
return $model;
}
},
'searchable_attributes' => ['name']
]);
}
now we will override our update and store methods to add the following :
$requested= collect($request->request);
if ($requested->has('assets_total')) {
$total_items=json_decode(str_replace('asset[]','asset',$requested['assets_total']));
$ids_to_save = [];
foreach ($total_items as $item){
$ids_to_save[$item->asset]['quantity'] = $item->quantity;
$ids_to_save[$item->asset]['cost'] = $item->cost;
}
$this->crud->getCurrentEntry()->assets()->sync($ids_to_save);
}
In the end we will our getter (Accesser) to access the data we want for our repeater
//Project Model
public function getAssetsTotalAttribute()
{
$records=DB::table('asset_project')->where('project_id' ,$this->id)->get();
$response=[];
foreach ($records as $record)
$response[]=[
'id'=>(String)$record->id,
'asset'=>(String)$record->asset_id,
'quantity'=>(String)$record->quantity,
'cost'=>(String)$record->cost,
];
return json_encode($response);
}
//You might not need the casting, but i added it just in case.
and that should make the repeater work fine and get all the data other than having the fields but not getting the data as you might have experienced.
If you have faced any issue with the code, feel free to comment below and i’ll reply back.