I have registration form with username, email, password and avatar(image) fields. Everything works except image filed, which can be null. I am using Vue as front-end and send data with axios to Laravel.
This is validation:
public function register(Request $request)
{
$request->validate([
'username' => 'required|string|max:255|unique:users',
'email' => 'required|string|email|max:255|unique:users',
'password' => 'required|string|min:6',
'avatar' => 'nullable|image|mimes:jpg,jpeg,png|max:1999'
]);
$fileNameToStore = 'noimage.jpg';
return User::create([
'username' => $request->username,
'email' => $request->email,
'password' => Hash::make($request->password),
'avatar' => $fileNameToStore
]);
}
And this is how I send data:
register(context, data) {
let formData = new FormData();
formData.append('avatar', data.avatar)
formData.append('username', data.username)
formData.append('email', data.email)
formData.append('password', data.password)
return new Promise((resolve, reject) => {
axios.post('/register', formData,{
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
resolve(response)
})
.catch(error => {
reject(error)
})
})
}
If I fill out every filed it works fine and also other things like checking extension or file size works, only problem is when I don't select any image. I have nullable included in validation, but I still get error message from validation that it must be an image and which extension it needs to have.
If your data.avatar is undefined or null, your server will eiter receive a undefined or null string as a value for your avatar field. Therefore, Laravel will be testing the image rule on the string.
To fix it, you can make sure your image is not undefined to send it in your request.
if (data.avatar) {
formData.append('avatar', data.avatar);
}
Or
formData.append('avatar', data.avatar ? data.avatar : '');
this is because of data.avatar send to backend as an empty string and
you can write a watch for data.avatar, which every time data.avatar is empty string change it to null . like the following code :
watch(){
'data.avatar'(value){
if(value == '' || value == undefined){
this.data.avatar = null
}
}
}
This is how I did mine and it worked for me very well, I just added a ref attribute on the input that should be the user avatar and then retrieved the value of the input through Vue as follows this.$refs.photo.files[0]:
In my HTML using Vue
<input id="image" class="opacity-0" type="file" ref="photo">
and in my js using Vue
var data = new formData();
if(this.$refs.photo.files.length > 0)
data.append('photo', this.$refs.photo.files[0]);
axios.post(...).then(...);
Related
I try to authenticate in Laravel9 Sanctum a SPA using vue3 and vuex, not with the token authentification but with the SPA Authentication.
I am not very used to the javascript language, and even less to promise chaining.
The first thing I am trying is registration.
Here are my methods.
The backend registration method
public function register(Request $request)
{
$request->validate([
'name' => 'required|string',
'email' => 'required|email|string|unique:users,email',
'password' => [
'required',
'confirmed',
Password::min(8)->mixedCase()->numbers()->symbols()
]
]);
$user=User::create([
'name' => $request['name'],
'email' => $request['email'],
'password' => bcrypt($request['password'])
]);
return response (['user'=>$user]);
}
The frontend registration method in the register.vue
//is actually the register form's data
const user = {
name: "",
email: "",
password: "",
password_confirmation: "",
};
function register(ev) {
ev.preventDefault();
store
.dispatch("register", user)
.then((data) => {
console.log("data in vue");
console.log(data);
router.push({
name: "Login",
});
})
.catch((error) => {
if (error.response.status === 422) {
errors = error.response.data.errors;
}
});
}
the actions method in the store/index.js
actions: {
register({ commit }, form) {
console.log("in register of index");
axiosClient.get("/sanctum/csrf-cookie");
return axiosClient.post("/api/register", form).then(({ data }) => {
console.log("data dans index");
console.log(data);
return data;
});
},
...
The registration is working fine but when I try an already existing email in the registration form, I get a status 422 as expected and this response from the axiosClient.post('/api/register',form):
{"message":"The email has already been
taken.","errors":{"email":["The email has already been taken."]}}
I expect this error to be intercepted by the catch in the register view but it doesn't happen. Despite this error I continue to use the .then and to push the Login route which is not what I want.
Can somebody tell me where I am doing wrong ?
I completely forget that in my axios.js there were interceptors that triggered this wrong behavior.
All is clear now.
I have a nuxt frontend using the vue-filepond adapter, users have the option to upload images with there post. This is then send to a laravel API that will handle the request.
<client-only>
<file-pond
name="image"
ref="pond"
class="filepond"
:allow-multiple="false"
accepted-file-types="image/jpeg, image/png"
server="http://127.0.0.1:8000/api/posts"
allowRevert="false"
:files="form.image"
/>
</client-only>
using mostly default filepond options,
data() {
return {
errors: [],
form: {
title: '',
content: '',
image: [],
}
}
},
Data is uploaded to the api like so
methods: {
createPost() {
this.$axios.$post('http://127.0.0.1:8000/api/posts', this.form)
this.$toast.show({
type: 'success',
title: 'Success',
message: 'Your post has been created'
})
}
}
Now since filePond is async the file is uploaded earlier then my form when I post it.
so in the laravel part
public function store(Request $request): void
{
if ($request->hasFile('image')) {
$path = Storage::putFile('avatars', $request->file('image'));
}
$request->validate([
'title' => 'required|string|max:24',
'content' => 'required|string|max:254',
'image' => 'nullable|image'
]);
Post::create([
'title' => $request->get('title'),
'slug' => Str::slug($request->get('title'), '-'),
'content' => $request->get('content'),
'image' => $path ?? null
]);
}
The image would be stored, but if I click submit on my form to upload a title and some content the ìmage part in the Post::create method is always NULL.
How can I make it so that filePond is not uploaded async anymore? so that when I hit submit on my form the title , content and image are all uploaded equally
Figured it out thanks to kissu and reading through filePond's docs.
const file = this.$refs.pond.getFiles()[0].file
const data = new FormData()
data.append('title', this.form.title)
data.append('content', this.form.content)
data.append('image', file)
this.$axios.$post('http://127.0.0.1:8000/api/posts', data)
and in your backend (laravel in my case)
if ($request->hasFile('image') && $request->file('image')->isValid()) {
$post->addMediaFromRequest('image')->toMediaCollection('image');
}
I am using Laravel 7 and Vue.js 2.
When I edit a room I should update the rooms table and then to redirect to the admin page with a succesfull message.
Unfortunately when I submit the form I edit correctly the table but then the redirect fails. It appears the following error message:
message: "The PUT method is not supported for this route. Supported methods: GET, HEAD.
This is my two methods in AdminController:
public function index()
{
$permissions = Permission::select('id')->get();
$rooms = Room::all();
$languages = Language::all();
$users = UserResource::collection(User::all());
return view('admin')->with(['success_message' => '', 'permissions'=>$permissions, 'users'=>$users, 'rooms'=>$rooms, 'languages'=>$languages]);
}
public function edit_room (Request $request) {
$validator = Validator::make($request->all(), [
'id' => 'required',
'name' => 'required'
]);
if ($validator->fails()) {
return response($validator->errors());
}
$room = Room::find($request->id);
$room->name = $request->name;
$room->save();
$success_message = "The room " . $request->name . " has been correctly edited";
return Redirect::route('admin')->with( ['success_message' => $success_message] );
}
This is the axios call in my child component:
editRoom: function() {
axios.put('edit_room', { id: this.rooms[this.index].id, name: this.roomName })
.then((response) => {
console.log(response);
this.errors = response.data;
if (Object.keys(this.errors).length === 0) {
alert('viva');
this.user = {};
} else {
alert('noviva');
}
})
.catch(error => {
alert(noooooo);
console.log(error);
});
}
This is my two routes in web.php:
Route::put('edit_room', 'AdminController#edit_room')->name('edit_room');
Route::get('/admin', 'AdminController#index')->name('admin');
This is an update of the table so I suppose I should use a PUT method but for some reason it doesn't.
Which is the best way to solve this error?
I think the problem is that you send your request via XHR
So when you using
return Redirect::route('admin')->with( ['success_message' => $success_message]
it sends an response with 301 http code to redirect your browser
i think you should refactor your code like this for example
return 'success_message'
and then in your axios after console.log(response);
window.location.href = "http://www.your_address.com/admin";
I am using Laravel 7 and Vue.js 2.
I made a form that should show a table if every field has been inserted.
I made also a server-side validation to check if all fields are correctly inserted.
This is the function that creates a call to the server with Axios:
runReport: function() {
const url = "api/get_report?room="+this.formReport['room']+"&participant="+this.formReport['participant']+"&start="+this.formReport['start']+"&end="+this.formReport['end'];
axios.get(url)
.then((response) => {
console.log(response.data.data);
this.meetingsReport = response.data.data;
alert('viva');
this.$emit('passMeetings', this.meetingsReport);
this.$emit('success');
})
.catch(function(error) {
console.log(error.response.data);
this.errors = error.response.data; //problem
alert('noviva');
});
}
This is the validator in the controller:
$validator = Validator::make($request->all(), [
'room' => 'required',
'start' => 'required',
'end' => 'required',
'participant' => 'required',
]);
if ($validator->fails()) {
return response($validator->errors(), 422);
}
If everthing has been correctly inserted in the form I have no problems, but if I missed a field I am unable to store the errors in an empty object that I created called errors.
UPDATED: This is the response from the validation failed (status 422):
{
"room": [
"The room field is required."
],
"participant": [
"The participant field is required."
]
}
I suppose the problem is that I am unable to access to this.errors from the catch block of Axios.
this in the context of an anonymous function doesn't point to the vue instance, so you may need to bind this to the function or use arrow function within the catch block
.catch(error => {
console.log(error.response.data);
this.errors = error.response.data; //should not be any - problem
alert('noviva');
});
At the end I solved using the keyword self in the axios call. In this way I was able to connect the catch block with Vue.js component.
This is the code:
runReport: function() {
let self = this;
const url = "api/get_report?room="+this.formReport['room']+"&participant="+this.formReport['participant']+"&start="+this.formReport['start']+"&end="+this.formReport['end'];
axios.get(url)
.then((response) => {
console.log(response.data.data);
this.meetingsReport = response.data.data;
alert('viva');
this.$emit('passMeetings', this.meetingsReport);
this.$emit('success');
this.errors = {};
})
.catch(function(error) {
console.log(error.response.data);
self.errors = error.response.data;
self.$emit('failure');
});
}
In my update form i have a file field that i want to update so i'm using following code and created a form object but when i pass form object i cannot get the data of form object in my controller.
let formData = new FormData();
formData.append('name', this.form.name);
formData.append('description', this.form.description);
formData.append('file_update', this.form.file_update);
axios.put(`/api/saveClass/${this.ItemId}`,formData,{headers:header})
.then((response)=>{
var msg = response.data.message;
if(response.status ==200){
this.$Progress.finish();
}else{
this.$Progress.fail();
}
this.$router.push(`/api/showClass`);
})
.catch(() => {
});
},
Laravel Controller:
public function update(Request $request,$id)
{
$ItemStore = class::find($id);
$this->validate($request,[
'name' => 'required|string|max:191|unique:class,name,'.$id,
'description' => 'required',
]);
}
I am getting name and description field is required but i'm passing data to it.