How protect public Laravel livewire property from manipulation? - laravel

I am mounting my component with an object $form which I need to access between requests.
The data in $form is not secret, but should not be tampered with so I would like to make it a protected property. Though only public properties are preserved between requests leaving it exposed to front end.
If possible, how can I prevent manipulation on this public property?
I tried a check
public function updating($key, $value)
{
if($key === 'form') return;
}
But I don't think that really does anything.
Also, using sessions is not an alternative in my situation.
Any ideas?

In my opinion you have these options:
Do not store the data as public property, instead just pass it to
your view in the render() method.
public function render(){
return view('Livewire.nameofyourview ', [
'form' => YourDataSource::get()
]);
}
This will refetch the data on every interaction with your component. You can access this in your template as before as $form.
Make sure to remove $form as public property. Manipulating the $form data form client/user site isn't possible with this solution. Docs
Use validation rules if you need your user/client to manipulate the
data, but only ways you expect.
protected $rules = [
'form.name' => 'required|min:6',
'form.email' => 'required|email',
];
Full Example
Use Laravel cache to preserve data between requests. This technique
is useful if you can't refetch your data from the source, like when
it was passed to your Livewire component as parameter (<livewire:form-component :form="$form">).
/* Cache the data on component mount */
public function mount($form)
{
Cache::set($this->id."_form", $form, 60*10);
}
public function someFunction()
{
/* read the data form cache, if you need it again */
cache($this->id."_form");
}

Related

how to use multiple request class in rules method (Livewire)

protected function rules()
{
return (new FormRequest)->rules();
}
Like above code how to use multiple request classes?
In a technicality, you're simply not supposed to use form requests within Livewire. However, since you're only using the rules from the form request, all you'd need is an array_merge.
protected function rules()
{
return array_merge(
(new FormRequest)->rules(),
(new OtherRequest)->rules(),
);
}
protected function rules(){
return array_merge(
(new FormRequest)->rules(),
(new OtherRequest)->rules(),
);
}
This works fine if they are no errors in your rules, but cannot work for multiple forms. In multiple forms, clicking on one submit pushes the validation errors on the other form. You can try this on the new method you want to use for validation
public function updateProfile(FormRequest, $request){
$data = $this->validateOnly($request->rules());
Profile::create($data);
}

Laravel 9 Cannot UPDATE Data

CONTROLLER
public function update(Request $request)
{
DB::table('bahanbaku')->where('id', $request->id)->update([
'id_bb' => $request->id_bb,
'nama_bb' => $request->nama_bb,
'stok' => $request->stok
]);
dd($request);
return redirect('/bahanbaku');
}
Sorry for my bad english, but in my case,
After Submit, Data still same, not change
Can you help me? T.T
Id does not exist, this is quite basic, but i feel like there is some misunderstandings with Laravel. Just to get the basics down, in Laravel i would expect you to do the following.
Use models for database operations
Instead of passing the id on the request, pass it on URL parameters
Use model binding for getting your model.
Create your model, since it has a non standard table name define that. Add the properties you want to be filled when updating it as fillables.
use Illuminate\Database\Eloquent\Model;
class Bahanbaku extends Model {
protected $table = 'bahanbaku';
protected $fillables = [
'id_bb',
'nama_bb',
'stok',
];
}
In your route, define the model as a parameter.
Route::put('bahanbaku/{bahanbaku}', [BahanbakuController::class, 'update']);
Now the logic can be simplified to this, which will automatically handle if the model is not found and give you a 404 error.
public function update(Request $request, Bahanbaku $bahanbaku)
{
$bahanbaku->fill(
[
'id_bb' => $request->id_bb,
'nama_bb' => $request->nama_bb,
'stok' => $request->stok
],
);
$bahanbaku->save();
return redirect('/bahanbaku');
}
To improve even more, i would look into utilizing form requests.

laravel model, adding attribute to model

I have a user model, and I want to add (an attribute to the user model) the user's email that it was before it was updated.
before#email.com
new#email.com
Within the user model, I have this function, I can get the before email, I was wondering I can assign some fake attribute, so I can access it like: $user->beforeEmail
protected static function booted()
{
static::saved(function ($user) {
$user->beforeEmail = $user->original['email'];
});
}
$user->beforeEmail // before#email.com
The code above is not working but provided it to help illustrate what I am trying to accomplish.
You could check if the email address has changed just before storing the new email to the db. This can be accomplished by using the saving event instead of the saved event.
protected static function booted()
{
static::saving(function ($user) {
if($user->isDirty('email')){
$user->beforeEmail = $user->email
}
});
}
Note: Your code example will not save the changes automatically since the saved event is ran after executing the query. It's possible that your code works just by adding $user->save()
Are you trying to get this value in the model or in a different class? As what you have works with a few adjustments already.
protected static function boot(){
parent::boot();
static::saved(function($user){
$user->originalEmail = $user->original['email'];
}
}
You can access originalEmail if you update the model in a controller or other class, like so:
$user = User::find(1);
$user->update([
'email' => 'email#email.com'
]);
// $user, $user->originalEmail, $user->some_test_accessor all return the correct values
I've also tested with an accessor, just to verify and it still works as though the value is available in the model. I'm not sure where you're attempting to access this value, though.
public function getSomeTestAccessorAttribute(){
return $this->originalEmail;
}
Does any of this help?

How to validate related objects request data from a parent controller in Laravel?

I'm using Laravel 8.x and have a one-to-one and one-to-many relationship with models as follows.
class ServiceProvider extends Model
{
use HasFactory;
protected $guarded = [];
public function contact() {
return $this->hasOne('App\Models\Contact');
}
public function services() {
return $this->hasMany('App\Models\Service');
}
}
I'm using a single form to get all the data. The problem is both Contact and Service has their own validation to be done. I can duplicate the validation in the ServiceProviderController. But it seems ugly and violate DRY. Is there a way to call the ContactController and ServiceController to do the validation and return the validation result to be accessed by the ServiceProviderController?
As I guess you are doing something like:
/**
* Store a new service provider.
*
* #param Request $request
* #return Response
*/
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('services/create')
->withErrors($validator)
->withInput();
}
// Store The Service Provider...
}
If Yes, I would recommend using a form request validator to validate the request. So if the request is get passed your controller will be called. Also, you can use the same Request Validation rule for both controllers. you can read how to create and use one more here.
You can set up enumerator classes that will return validation rules for you, or even declare static properties on your model which will hold an array of validation rules, and which can be called like: ServiceProvider::$rules or something similar.
This way you will keep all your rules at one place. You can't explicitly call controllers whenever, they respond to routes.
When you get the validation rules, just use $request->validate() method and send the rules you gathered to it.

Laravel Event exceeds Pusher allowed limit

I have an event in my Laravel application that for specific records it exceeds the allowed maximum limit (10240 bytes) of Pusher. Is it correct that Laravel serializes every public attribute on the Event class? If so I suspect that the serialized model should not exceed the 10kb limit but it fails anyway. Are there any approaches to decrease the size of the data content?
class PostChanged implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public $post;
/**
* Create a new event instance.
*
* #return void
*/
public function __construct(Post $post)
{
$this->post = $post;
}
/**
* Get the channels the event should broadcast on.
*
* #return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
return new Channel('post-channel.'.$this->post->id);
}
public function broadcastWith()
{
$extra = [
'data' => $this->post->data,
];
return array_merge($this->post->toArray(), $extra);
}
}
produces:
The data content of this event exceeds the allowed maximum (10240 bytes).
See http://pusher.com/docs/server_api_guide/server_publishing_events for more info
Approach 1: Resolve at client side
The most reliable approach would be what #ExohJosh described: Only send the event type along with an ID so the client (most likely JavaScript) can fetch the updated record through a separate REST (or whatever) API.
public function broadcastWith()
{
return [
'id' => $this->post->id,
];
}
Approach 2: Reduce Payload
An alternative (and simpler) approach would be to send only the data required by the client (the one you figured out yourself #sarotnem). However this approach is only safe, if you definitely know that the attributes you submit can not in any case exceed the 10KiB limit. This can be ensured through input validation, limitations on DB columns or other means.
When choosing this approach, be sure to also include the size of any relationships, that could possibly be loaded on the model, into your calculations.
A neat way to define an "external representation" of a model are Laravel's API Resources. They could make your code look like this:
public function broadcastWith()
{
return [
'post' => new \App\Http\Resources\PostResource($this->post),
];
}
where App\Http\Resources\PostResource could be:
class PostResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'title' => $this->title,
'body' => $this->body,
];
}
}
An approach I have taken to this in the past when working with large objects is to consider segregation of the large object or pass a reference of the object EG: the id and then do the additional functionality in the event listener.
An approach in the case of a post changing could be:
The post is changed on client 1.
Backend lets pusher know the post has changed and receives the id
Pusher broadcasts to client 2
client 2 is listening and hits the endpoint to get a client by id
If this approach isn't going to work for you – You need to check if the object you are serialising has any redundancies in data, if you are passing too much there is an issue there.
After quite a lot of experimenting I've managed to get it working by simply unsetting some unnecessary values of the array produced by $post->toArray().
Also I noticed that the broadcastWith() method returns the payload as an array and not serialised.

Resources