F
Filament15mo ago
Kaan

Update Repeater

I've been working with the Repeater component and I've encountered an issue where changes made on the front-end are not being persisted to the database. Here's the code I'm using:
45 Replies
Kaan
KaanOP15mo ago
https://hastebin.skyra.pw/wudalakigo.php When I delete an item from the Repeater in the front-end and press save, the item disappears as expected. However, when I refresh the page, the item reappears. Similarly, when I reorder the items using drag and drop and hit save, the new order is not saved to the database on refresh. I am not sure why these changes are not persistent. Is there something I am missing about synchronizing frontend changes with the database? Any guidance would be much appreciated. Thank you! UP
Rupadana
Rupadana15mo ago
basically, u need to save changed by clicking a save button. not sure, but i think Repeater is not realtime to update your data
Kaan
KaanOP15mo ago
Yes you right. Could you check my code again? Because i updated few hours ago.
awcodes
awcodes15mo ago
Nothing is real-time unless you make it so. Just updating a field will not automatically persist it to the db.
Kaan
KaanOP15mo ago
Yes. You right. Could you check my code again? Because i updated few hours ago.
awcodes
awcodes15mo ago
Sorry. I’m not going to write your business logic for you. Automatic updates are not related to filament.
Kaan
KaanOP15mo ago
Sorry. I didn't understand And i updated my question now If you mean writing code for me, I don't want that. I solved part of the problem I opened the topic and that's why I updated my code. But now I have also updated the question.
awcodes
awcodes15mo ago
Ok. But are you actually saving the changes or are you expecting front end changes to be automatically persisted? There’s a difference.
Kaan
KaanOP15mo ago
I didn't explain my problem correctly. I'm sorry about that. Actually, the update process is working fine right now. Only position update is not happening. https://hastebin.skyra.pw/wudalakigo.php I made a few more updates. This is the final version of my code. I can't send it over discord because of the message limit.
public function form(Form $form): Form
{
return $form
->schema([
Repeater::make('categorySteps')
->label('Category Steps')
->schema([
TextInput::make('step_title')
->label('Step Title')
->required(),
])
->columns(2)
->reorderable()
->reorderableWithButtons()
->orderColumn('position')
->addable(false),
])
->columns(1)
->statePath('data');
}
public function form(Form $form): Form
{
return $form
->schema([
Repeater::make('categorySteps')
->label('Category Steps')
->schema([
TextInput::make('step_title')
->label('Step Title')
->required(),
])
->columns(2)
->reorderable()
->reorderableWithButtons()
->orderColumn('position')
->addable(false),
])
->columns(1)
->statePath('data');
}
But here is the problem
Rupadana
Rupadana15mo ago
if your data is not removed in database, it's cause your code.
public function save(): void
{
try {
$data = $this->form->getState();

foreach ($data['categorySteps'] as $index => $step) {
$categoryStep = CategoryStep::find($step['id']);
if ($categoryStep) {
$categoryStep->update(['position' => $index]);
}
}
} catch (Halt $exception) {
return;
}

Notification::make()
->success()
->title("It's Working!")
->send();
}
public function save(): void
{
try {
$data = $this->form->getState();

foreach ($data['categorySteps'] as $index => $step) {
$categoryStep = CategoryStep::find($step['id']);
if ($categoryStep) {
$categoryStep->update(['position' => $index]);
}
}
} catch (Halt $exception) {
return;
}

Notification::make()
->success()
->title("It's Working!")
->send();
}
awcodes
awcodes15mo ago
Is categorySteps a relationship?
Kaan
KaanOP15mo ago
yes Category Model:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;


class Category extends Model
{
use HasFactory;
protected $table = 'categories';
protected $fillable=['name','slug', 'description', 'creator_id', 'is_active'];

protected static function boot() {
parent::boot();
static::creating(function ($model) {
$model->slug = Str::slug($model->name);
$model->creator_id = auth()->user()->id;
});

static::updating(function ($model) {
$model->slug = Str::slug($model->name);
$model->creator_id = auth()->user()->id;
});
}

//Relation with CategoryStep
public function categorySteps()
{
return $this->hasMany(CategoryStep::class);
}


}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;


class Category extends Model
{
use HasFactory;
protected $table = 'categories';
protected $fillable=['name','slug', 'description', 'creator_id', 'is_active'];

protected static function boot() {
parent::boot();
static::creating(function ($model) {
$model->slug = Str::slug($model->name);
$model->creator_id = auth()->user()->id;
});

static::updating(function ($model) {
$model->slug = Str::slug($model->name);
$model->creator_id = auth()->user()->id;
});
}

//Relation with CategoryStep
public function categorySteps()
{
return $this->hasMany(CategoryStep::class);
}


}
CategoryStep Model:
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class CategoryStep extends Model
{
use HasFactory;

const TYPE_UPLOAD = 1;

const TYPE_REQUEST = 2;


protected $table = 'category_steps';
protected $fillable=['category_id', 'position', 'type', 'step_title', 'file', 'file_description', 'is_active'];

protected static function booted()
{
static::creating(function ($categoryStep) {
$lastPosition = self::max('position');
$categoryStep->position = $lastPosition + 1;
});
}



//Relation with Category
public function category()
{
return $this->belongsTo(Category::class);
}
}
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class CategoryStep extends Model
{
use HasFactory;

const TYPE_UPLOAD = 1;

const TYPE_REQUEST = 2;


protected $table = 'category_steps';
protected $fillable=['category_id', 'position', 'type', 'step_title', 'file', 'file_description', 'is_active'];

protected static function booted()
{
static::creating(function ($categoryStep) {
$lastPosition = self::max('position');
$categoryStep->position = $lastPosition + 1;
});
}



//Relation with Category
public function category()
{
return $this->belongsTo(Category::class);
}
}
awcodes
awcodes15mo ago
Well there you go. You aren’t saving it’s order anywhere on the relationship.
Rupadana
Rupadana15mo ago
u just update it here, there is not sync function
Kaan
KaanOP15mo ago
No description
awcodes
awcodes15mo ago
But without an order column when it’s read back out of the database there’s no way for the repeater to know what order to display it in.
Kaan
KaanOP15mo ago
I think I need to study a little more about this subject. Is there a resource you can recommend? This is the first time I'm updating the position.
awcodes
awcodes15mo ago
A normal repeater stores it’s data in an array so orderable works in that context, but if it is a relationships it has no context of order if you don’t set that up. An array sorted as data into a single field in the db will honor its order. But a relationship is multiple individual records in a separate table in the db. So if order matters you also have to have a column in that table that says this is it’s order in the returned records. And also store that order number for each record when the form is saved. It won’t be automatic in this context. You’ll have to do a little extra work to make it work. But that is outside the scope of filament and has more to do with saving the data in the way you need it be saved. If you handle that appropriately then filament will work.
Kaan
KaanOP15mo ago
Yes it makes sense to create a new table for position. It's true that I have a relational database. However, I can call the "categorySteps" I use in my code directly to a variable from outside and the position is in the categorySteps table. So I can work with the categorySteps table independently of the category table.
Rupadana
Rupadana15mo ago
i think its better to use mutateFormDataBeforeSave method than override save method
awcodes
awcodes15mo ago
So, if you’ve set that up correctly there is a ->relationship() modifier on the Repeater whose third parameter is a query modifier that you can use to get the records in the order that you need to display them in.
Kaan
KaanOP15mo ago
Could you send me examples or documentation page?
Kaan
KaanOP15mo ago
No description
No description
Kaan
KaanOP15mo ago
The two are relational tables. However, I didn't use a relationship method because I don't think there is a situation that requires them to work together in this scenario. Therefore, doesn't the repeater not working with a relationship make it workable?
awcodes
awcodes15mo ago
I think you’re expecting the field to assume things about your business logic. But queries don’t work that way.
Kaan
KaanOP15mo ago
worst case I will create an additional table for positions. Is there any documentation or a source on how I can use this?
awcodes
awcodes15mo ago
You can’t just say give me all the posts in the db and expect them to be in a certain order. You have to query them in the order you want. You don’t need a whole new table you just need a field in the table that you can set to an order.
Kaan
KaanOP15mo ago
but i have
Kaan
KaanOP15mo ago
No description
awcodes
awcodes15mo ago
Like if you want the records based on when they were created you have to query them based on the created_at field. It’s no different.
Kaan
KaanOP15mo ago
I guess I didn't explain it well, I'll try to explain it in more detail. I'm facing a challenge in updating the position field of related records using the Repeater component in Filament. I'd appreciate any insights or suggestions to address this issue. System Overview: Models & Relationships: I have a Category model. Each Category can have multiple CategoryStep models associated with it. The relationship is defined as Category hasMany CategorySteps. Purpose: I'm trying to manage the order of steps within a category using the Repeater component in a custom Filament page. Each CategoryStep has a position field, which I intend to use to determine its order within its parent Category. Filament Setup: In the ManageCategorySteps page, I'm using the Repeater component to display and manage the steps of a specific category. The Repeater component is set up to be reorderable, and I'm using the orderColumn('position') method to specify that the position field in the CategoryStep model should be used for ordering. Code Snippet:
public function form(Form $form): Form
{
return $form
->schema([
Repeater::make('categorySteps')
->label('Category Steps')
->schema([
TextInput::make('step_title')
->label('Step Title')
->required(),
])
->columns(2)
->reorderable()
->reorderableWithButtons()
->orderColumn('position')
->addable(false),
])
->columns(1)
->statePath('data');
}
public function form(Form $form): Form
{
return $form
->schema([
Repeater::make('categorySteps')
->label('Category Steps')
->schema([
TextInput::make('step_title')
->label('Step Title')
->required(),
])
->columns(2)
->reorderable()
->reorderableWithButtons()
->orderColumn('position')
->addable(false),
])
->columns(1)
->statePath('data');
}
Issue: On the frontend, when I drag and reorder the steps using the Repeater component, they visually reorder as expected. However, after saving the form and refreshing the page, the position values in the categorySteps table remain unchanged. This results in the steps reverting to their original order. It seems like the reordered data from the form isn't being passed to the backend correctly or isn't being saved to the database as expected. Questions: Is there a specific way to handle the saving of reordered records in a related model using the Repeater component? Are there any known issues or additional configurations required to ensure that the position values in the related CategoryStep model are updated correctly?
Rupadana
Rupadana15mo ago
/**
* @param array<string, mixed> $data
* @return array<string, mixed>
*/
protected function mutateFormDataBeforeSave(array $data): array
{

// Update the position of $data['repeaters'] here

return $data;
}
/**
* @param array<string, mixed> $data
* @return array<string, mixed>
*/
protected function mutateFormDataBeforeSave(array $data): array
{

// Update the position of $data['repeaters'] here

return $data;
}
Rupadana
Rupadana15mo ago
but u need to use ->relationship()
awcodes
awcodes15mo ago
Well, you model is only setting the order on creating observer of a record, so when updating a record the order won’t be changed.
Kaan
KaanOP15mo ago
Yes Actually, I was hoping that the repeater would do it automatically for me.
Rupadana
Rupadana15mo ago
This helped your problem
awcodes
awcodes15mo ago
Based on what you’ve shared I feel like you’re overcomplicating all of this.
Kaan
KaanOP15mo ago
Reviewing You are absolutely right. I'm little burnout
awcodes
awcodes15mo ago
But yes. The repeater isn’t going to save an order automatically with a relationship. It will without a relationship though. Sleep on it then comeback at it tomorrow.
Kaan
KaanOP15mo ago
This is the first thing I will do after I solve this bug. But right now I feel that I am very close to the solution. How should I update getFormActions when I use this method and remove the save method?
awcodes
awcodes15mo ago
You would have a column on your table for the order then in the mutateFormDataBeforeSave() you would use $state to loop the repeater items to set the order column for the items to the key of the array. Then return the modified data.
Kaan
KaanOP15mo ago
Finally my code works fine. I solved the problem, but I don't know how. I will never open this file again. Here is my code: https://hastebin.skyra.pw/vapogakasu.php Thank you very much for your time. @awcodes @Rupadana
awcodes
awcodes15mo ago
Glad you got it working the way you need.

Did you find this page helpful?