Trying to upload an image using laravel and vuejs - laravel

I have my upload html form like this
<div class="album_single_data settings_data">
<div class="album_single_img">
<figure class="thumbnail thumbnail-rounded">
<img class="main-profile" :src="getprofilepicture()" alt="">
</figure>
</div>
<div class="album_single_text">
<div class="album_btn profile_image">
<div class="btn btn-primary">
<input class="settings-file-upload" type="file" #change="updateProfilePic" ref="file" accept="image/*">
<div class="settings-file-icon">
<span class="material-icons">camera_alt</span>
Upload Profile Picture. (Note: Must Be Less Than 2MB)
</div>
</div>
</div>
</div>
</div>
<div class="form-group">
<button #click="updateInfo" class="btn btn-primary">Update Profile</button>
</div>
This is the vuejs code that handles the form submit/upload method which means anytime i click on the upload button the image uploads. but the problem is that it does not submit
export default {
name: 'Settings',
components: {
//
},
data() {
return{
form: new Form ({
id: '',
username:'',
email: '',
password: '',
name: '',
bio: '',
twitter_handle: '',
facebook_handle: '',
instagram_handle: '',
backgroundPicture: ''
}),
pic: ({
profilePicture: '',
})
}
},
created(){
axios.get("profile")
.then(({ data })=>(this.form.fill(data)))
.then(()=>(this.pic.data))
;
},
methods: {
getprofilepicture()
{
//default avatar pic if there is no photo of user
let profilePicture = "http://localhost:8000/img/profile/user.png";
//returns the current path of the
if (this.pic.profilePicture) {
if (this.pic.profilePicture.indexOf('base64') != -1) {
profilePicture = this.pic.profilePicture;
} else {
profilePicture = 'http://localhost:8000/img/profile/' + this.pic.profilePicture;
}
return profilePicture;
}
return profilePicture;
//let profilePicture = (this.form.profilePicture.length > 200) ? this.form.profilePicture : "http://localhost:8000/img/profile/"+ this.form.profilePicture ;
//return profilePicture;
},
// getBackgroundPic(){
// let backgroundPicture = "http://localhost:8000/img/profile/background.jpg";
// if(this.form.backgroundPicture){
// if(this.form.backgroundPicture.indexOf('base64') != -1){
// backgroundPicture = this.form.backgroundPicture;
// }else {
// backgroundPicture = 'http://localhost:8000/img/profile/'+ this.form.backgroundPicture;
// }
// return backgroundPicture;
// }
// return backgroundPicture;
// },
updateInfo(){
this.$Progress.start();
this.form.put('profile')
.then(()=> {
this.$Progress.finish();
this.$router.go('/profile')
})
.catch(()=>{
this.$Progress.fail()
})
},
updateProfilePic(e){
let file = e.target.files[0];
//console.log(file);
let reader = new FileReader();
if(file['size'] < 2097152){
reader.onloadend = () => {
//console.log('RESULT', reader.result)
this.pic.profilePicture = reader.result;
}
reader.readAsDataURL(file);
}else{
Toast.fire({
icon: 'error',
title: 'Ooops...',
text: 'The file you are trying to upload is more than 2MB',
})
}
},
updateBackgroundPic(e){
let file = e.target.files[0];
//console.log(file);
let reader = new FileReader();
if(file['size'] < 2097152){
reader.onloadend = () => {
this.form.backgroundPicture = reader.result;
}
reader.readAsDataURL(file);
}else{
Toast.fire({
icon: 'error',
title: 'Ooops...',
text: 'The file you are trying to upload is more than 2MB'
})
}
}
}
}
</script>
Anytime i click on the submit button i have this error message: "Undefined offset: 1", exception: "ErrorException",…}
exception: "ErrorException"
and i really do not know the cause of this error.
Below is the PHP Code that handles the server side part of the upload
public function updateProfile(Request $request)
{
$user = auth('api')->user();
$this->validate($request,[
'username' => 'required|string|max:255|unique:users,username,'.$user->id,
'name' => 'max:255',
'email' => 'required|string|email|max:255|unique:users,email,
'.$user->id,
'password' => 'sometimes|required|min:8'
]);
$currentProfilePicture = $user->profilePicture;
if($request->profilePicture != $currentProfilePicture){
$name = time().'.' . explode('/', explode(':', substr($request->profilePicture, 0, strpos($request->profilePicture, ';')))[1])[1];
Image::make($request->profilePicture)->save(public_path('img/profile/').$name);
$request->merge(['profilePicture' => $name]);
$userPhoto = public_path('img/profile/').$currentProfilePicture;
if(file_exists($userPhoto)){
#unlink($userPhoto);
}
}
$user->update($request->all());
}

Related

Obtain values from Laravel Controller and display in Vue

In a Laravel 8 view, I have a Vue component with a form.
<contact-form-component contact-store-route="{{ route('contact.store') }}">
</contact-form-component>
ContactFormController.vue
<template>
<div>
<div v-if="success">
SUCCESS!
</div>
<div v-if="error">
ERROR!
</div>
<div v-show="!success">
<form
#submit.prevent="storeContact"
method="POST"
novalidate="novalidate"
#keydown="clearError"
>
<input type="text" name="fullname" v-model="formData.fullname" />
<input type="text" name="email" v-model="formData.email" />
<input type="text" name="phone" v-model="formData.phone"/>
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import Form from "./Form.vue";
export default {
mixins: [Form],
props: {
contactStoreRoute: String,
},
data() {
return {
formData: {
fullname: null,
lname: null,
email: null,
phone: null,
message: null,
},
};
},
methods: {
storeContact() {
this.post(this.contactStoreRoute, this.formData);
},
},
mounted() {},
};
</script>
Form.vue
<template></template>
<script>
import FormErrors from "./FormErrors.vue";
export default {
name: "Form",
mixins: [FormErrors],
data() {
return {
success: false,
error: false,
errorMessage: "",
};
},
methods: {
post(url, data) {
this.success = false;
this.error = false;
axios
.post(url, data)
.then((res) => {
this.onSuccess(res.data.message);
})
.catch((error) => {
if (error.response.status == 422) {
this.setErrors(error.response.data.errors);
} else {
this.onFailure(error.response.data.message);
}
});
},
get(url, data) {
this.success = false;
this.error = false;
axios
.get(url, data)
.then((res) => {
this.onSuccess(res.data.message);
})
.catch((error) => {
if (error.response.status == 422) {
this.setErrors(error.response.data.errors);
} else {
this.onFailure(error.response.data.message);
}
});
},
onSuccess(message) {
this.reset();
this.success = true;
},
onFailure(message) {
this.error = true;
this.errorMessage = message;
},
reset() {
this.clearAllErrors();
for (let field in this.formData) {
this.formData[field] = null;
}
},
},
};
</script>
FormErrors.vue
<template></template>
<script>
export default {
name: "FormErrors",
data() {
return {
errors: {},
};
},
methods: {
setErrors(errors) {
this.errors = errors;
},
hasError(fieldName) {
return fieldName in this.errors;
},
getError(fieldName) {
return this.errors[fieldName][0];
},
clearError(event) {
Vue.delete(this.errors, event.target.name);
},
clearAllErrors() {
this.errors = {};
},
},
computed: {
hasAnyError() {
return Object.keys(this.errors).length > 0;
},
},
};
</script>
When the form is submitted, a laravel post route is called and the information is stored in the database.
Route::post('/contact/store', [ContactController::class,'store'])->name('contact.store');
After this, the Vue component now hides the form and displays a "success" message. So far, everything works great.
Now, I would like to add a step. Instead of success message, I want to obtain the last id entered in the db and show a new form with a hidden field last_id. I am unsure of how to obtain this information from the controller.
It would be a continuation of the previous form, but I do not want to gather all the data at once, I want it in steps. Now, it is also important to gather data from the first form, and if the user quits after the first form that is fine, no problem, but if the user continues with the second form I need to "link" it to the previous form through the last_id.
I think that I am not approaching this problem correctly, maybe I need to change the logic of what I am doing.
Adding last_id to ContactController return:
class ContactController extends Controller
{
public function store(Request $request)
{
$data = $request->validate([
'fullname' => 'required',
'email' => 'required|email',
'phone' => 'required',
]);
$contact_form = Contact::create($data);
$last_id = $contact_form->id;
return [
//how do I "send" this to the Vue component?
'last_id' => $last_id
];
}
}
This would be the "second step" form using the last ID. It would have it's own post route.
<h4>Your form has been successfully submitted, now please give us more info that will be linked to the previous form:</h4>
<form>
<input type="hidden" name="last_id" value="{HOW_TO_GET_THE_LAST_ID_HERE?}" />
<textarea name="message" required></textarea>
<input type="submit" value="Submit" />
</form>
You can use a JSON response:
https://laravel.com/docs/9.x/responses#json-responses
You can return the id in your Controller for example like this:
return response()
->json(['last_id' => $last_id])
You will be able to get the value from the response object of the post call
.then((res) => {
//get the id from the response
this.last_id = (res.data.last_id);
})

How to check if a laravel validator response is an error or not with Vue.js

I am using Laravel 7 and Vue.js 2.
I make a delete call with axios and if everything is correct I receive as a response the new data in the related tables to update a select and if there is an error I receive the errors of the Laravel validator.
The problem is that I have to understand with javascript if the response is an error or not... but I don't know how to do that.
This is my Vue component:
<template>
<form method="DELETE" #submit.prevent="removeTask">
<div class="form-group">
<title-form v-model="titleForm" :titleMessage="titleForm"></title-form>
</div>
<hr>
<div class="form-group">
<label for="tasks">Tasks:</label>
<select required v-model="user.tasks" class="form-control" id="tasks" #mouseover="displayResults(false, false)">
<option v-for="task in tasks_user" :value="task.id" :key="task.id">
{{ task.task_name }} - {{ task.task_description }}
</option>
</select>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
<hr>
<div class="form-group">
<validated-errors :errorsForm="errors" v-if="displayErrors===true"></validated-errors>
<!--<success-alert :success_message="successMessage" v-if="displaySuccess===true"></success-alert>-->
</div>
</form>
</template>
<script>
import ValidatedErrors from "./ValidatedErrors.vue"
import SuccessAlert from "./SuccessAlert.vue"
import TitleForm from "./TitleForm.vue"
export default {
components: {
'validated-errors': ValidatedErrors,
'success-alert': SuccessAlert,
'title-form': TitleForm
},
mounted() {
console.log('Component mounted.');
},
props: {
tasks_user: {
type: Array,
required: true,
default: () => [],
}
},
computed: {
titleForm: function () {
return "Remove a task from " + this.tasks_user[0].user_name;
}
},
data: function() {
return {
user: {
tasks: ""
},
errors: {},
displayErrors: false,
displaySuccess: false,
successMessage: "The task has been removed."
}
},
methods: {
removeTask: function() {
alert(this.user.tasks);
//axios.delete('/ticketsapp/public/api/remove_task_user?id=' + this.user.tasks)
axios.delete('/ticketsapp/public/api/remove_task_user?id=' + 101)
.then((response) => {
console.log(response.data);
if(typeof response.data[0].task_id !== "undefined") {
alert("There are no errors.");
} else {
alert("There are errors.");
}
if (typeof response.data[0].task_id === "undefined") { //problem
alert('noviva');
console.log(response.data);
this.errors = response.data;
this.displayErrors = true;
} else {
alert('viva');
this.tasks_user = response.data;
this.errors = {};
}
})
.catch(error => {
alert(noooooo);
console.log(error);
});
},
displayResults(successShow, errorShow) {
this.displayErrors = errorShow;
this.displaySuccess = successShow;
}
},
}
</script>
This is my method in the controller:
public function remove(Request $request) {
$validator = Validator::make($request->all(), [
'id' => 'required|exists:task_user,id'
]);
if ($validator->fails()) {
return response($validator->errors()); //problem
}
$task_user_id = $request->id;
$user_id = TaskUser::where('id', $task_user_id)->pluck('user_id')->first();
TaskUser::find($task_user_id)->delete();
$tasks_user = TaskUser::with(['user', 'task'])->get();
$tasks_user = TaskUser::where('user_id', $user_id)->get();
$tasks_user = TaskUserResource::collection($tasks_user);
return json_encode($tasks_user);
}
To distinguish the type of return I created this condition: if (typeof response.data[0].task_id === "undefined") but when that condition is true everything falls down and I receive the following error in the console:
Uncaught (in promise) ReferenceError: noooooo is not defined
So how can I do to distinguish the type of return of the API call?

Vue.js with Laravel 8 API image upload throws 500 error

Below is this code for the image upload path; this is my template upload code.
<div class="form-group">
<div class="form-row">
<div class="col-md-6">
<input #change="onFileSelected" class="custom-file-input" type="file">
<small class="text-danger" v-if="errors.photo"> {{errors.photo[0]}} </small>
<label accept="image/*" class="custom-file-label" for="customFile" id="photo">Choose file</label>
</div>
<div class="col-md-6">
<img :src="form.photo" style="height: 40px; width:40px;">
</div>
</div>
</div>
This is the click event's function; this is the data I want to pass to my API.
ata() {
return {
form:{
name: null,
email: null,
address: null,
salary: null,
joining_date: null,
nid: null,
phone: null,
photo: null
},
errors: {
}
}
},
methods: {
onFileSelected(event){
// console.log(event)
let file = event.target.files[0];
if(file.size > 1048770) {
// Notification.image_validation()
Toast.fire({
icon: 'error',
title: 'upload image less than 1MB'
})
}else {
//console.log(form)
let reader = new FileReader();
reader.onload = event =>{
this.form.photo = event.target.result
console.log(event.target.result)
};
reader.readAsDataURL(file)
}
},
The following is the code I push my date to.
employeeinsert()
{
console.log(this.form.photo)
axios.post('/api/employee', this.form)
.then(() => {
// console.log(this.form)
// this.$router.push({name: 'employee'})
// Toast.fire({
// icon: 'success',
// title: 'employee add success'
// })
})
.catch(error => this.errors = error.response.data.errors)
}
These are the Laravel 8 backend validations. Here I try to get the request image and name change and try to save my public folder.
if ($request->photo) {
$position = strpos($request->photo, ';');
$sub = substr($request->photo, 0, $position);
$ext = explode('/', $sub)[1];
$name = time() . "." . $ext;
$img = Image::make($request->photo)->resize(240, 200);
$upload_path = 'backend/employee';
$inage_url = $upload_path . $name;
$img->save($inage_url);
return console . log($inage_url);
Employee::insert([
'name' => $request->name,
'email' => $request->email,
'address' => $request->address,
'salary' => $request->salary,
'joining_date' => $request->joining_date,
'nid' => $request->nid,
'phone' => $request->phone,
'photo' => $last_image
]);
When I upload an image, I get a 500 error.
Here's the 500 error with any logs.
I have no idea why it throws 500. Please help to resolve this.
Few things which can cause 500 error as visible in your code are
return console.log($inage_url): console.log() is javascript not PHP
$inage_url = $upload_path.$name; it should be $inage_url = $upload_path.'/'.$name otherwise $img->save($inage_url); can cause error as path is not well formed
Not clear where the $last_image comes from in'photo' => $last_image

Unable to upload a file using Vue.js to Lumen backend?

I have tried to upload a file using vue.js as front end technology and laravel in the back end. I have tried to pass the file object using formData javascript object but the server responds as the value is not passed.
I have tried to log the file using console.log and it appropriately displays the data.
Consider that I have discarded some field names.
Template Code
<template>
<b-container>
<div align="center">
<b-card class="mt-4 mb-4 col-md-8" align="left" style="padding: 0 0;">
<card-header slot="header" />
<b-form>
<div class="row">
<div class="col-6 col-md-6">
<b-button
type="submit"
variant="success"
class="float-right col-md-5"
v-if="!update"
#click="save"
squared
>
<i class="fas fa-save"></i>
Save
</b-button>
</div>
</div>
<hr style="margin-top: 10px;" />
<b-form-group
label-cols="12"
label-cols-lg="3"
label-for="input-2"
label="Remark: "
label-align-sm="right"
label-align="left"
>
<b-form-textarea
id="textarea"
v-model="record.remark"
rows="2"
max-rows="3"
></b-form-textarea>
</b-form-group>
<b-form-group
label-cols="12"
label-cols-lg="3"
label-for="input-2"
label="Remark: "
label-align-sm="right"
label-align="left"
>
<b-form-file
v-model="record.attachement"
:state="Boolean(record.attachement)"
placeholder="Choose a file..."
drop-placeholder="Drop file here..."
></b-form-file>
</b-form-group>
</b-form>
<status-message ref="alert" />
</b-card>
</div>
</b-container>
</template>
Script Code
<script>
import { mapGetters, mapActions } from "vuex";
export default {
props: ["id", "user_id"],
data: () => ({
record: {
remark: "",
attachement: null
}
}),
methods: {
...mapActions([
"addBenefitRequest",
]),
save(evt) {
evt.preventDefault();
this.$validator.validate().then(valid => {
if (valid) {
const Attachement = new FormData();
Attachement.append("file", this.record.attachement);
var object = {
remark: this.remark
};
this.addBenefitRequest(object, Attachement);
}
});
},
},
computed: mapGetters([
"getStatusMessage",
"getBenefitRequest",
])
};
</script>
Store Code
async addBenefitRequest({ commit }, object, Attachement) {
try {
const response = await axios.post(
commonAPI.BENEFIT_BASE_URL + "/benefit-requests",
object,
Attachement,
{
headers: {
"Content-Type": "multipart/form-data"
}
}
);
commit("pushBenefitRequest", response.data);
commit("setStatusMessage", "Record has been added.");
} catch (error) {
return error
},
Controller Code
public function store(Request $request, Request $request2)
{
$this->validate($request, [
'employee_id' => 'required|string',
'requested_date' => 'required|date',
// 'benefit_type_id' => 'required|string|exists:benefit_types,id',
'reason' => 'required|string',
]);
$this->validate($request2, [
'attachement' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
]);
// $success = BenefitRequest::exists($request->employee_id);
// if(!$success)
// return response()->json("Employee doesn't exist", 422);
$id = (string) Str::uuid();
if($request2->attachement)
{
$attachement = $request2->file('attachement')->store('Benefits');
$request->merge(['attachement' => $attachement]);
}
// $request->attachement = $request->file('attachement')->store('Benefits');
$request->merge(['id' => $id]);
BenefitRequest::create($request->all());
return response()->json('Saved', 201);
}
Route
$router->post('',
['uses' => 'BenefitRequestController#store',
'group'=>'Benefit requests',
'parameter'=>'employee_id, requested_date, requested_by, benefit_type_id, reason, remark, status',
'response'=>'<statusCode, statusMessage>'
]);
Here is an example. you can try it
index.vue
`<div id="app">
<div v-if="!image">
<h2>Select an image</h2>
<input type="file" #change="onFileChange">
</div>
<div v-else>
<img :src="image" />
<button #click="removeImage">Remove image</button>
</div>
</div>`
new Vue({
el: '#app',
data: {
image: ''
},
methods: {
onFileChange(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
this.createImage(files[0]);
},
createImage(file) {
var image = new Image();
var reader = new FileReader();
var vm = this;
reader.onload = (e) => {
vm.image = e.target.result;
};
reader.readAsDataURL(file);
},
removeImage: function (e) {
this.image = '';
}
}
})

How to upload photo using Laravel RestApi and vuejs

I'm working on a RestFulApi using Laravel and Vuejs, now want upload a photo using RestfulApi and vuejs. Here comes my sample code:
<div class="form-group form-group-default float-left required">
<label for="photo">Photo</label>
<input class="file-input" type="file" ref="photo" name="photo" v-
on:change="addFile($event)">
</div>
data(){
return{
films_info:
{
photo: null,
}
}
},
methods: {
addFile: function(e){
let that = this;
that.films_info.photo = this.$refs.photo.files[0];
},
saveFilms: function(){
let that = this;
axios.post('/api/films/save_films',that.films_info)
.then(function (response) {
location.reload(true);
})
.catch(function (error) {
that.errors = error.response.data.errors;
console.log("Error Here: "+error.response.data);
});
}
}
protected function saveFilms(Films $request)
{
if ( $request->photo ) {
$extension = $filmsObj->photo->getClientOriginalExtension();
$request->photo = 'films_'.\Carbon\Carbon::now().'.'.$extension; // renaming image
$request->photo->move($dir_path, $request->photo);
}
}
Here in this code I get error in getClientOriginalExtension() method call. It says:
getClientOriginalExtension() method called on string.
Finally I managed to solved the photo upload issue using VueJS and Laravel Api call.
<div class="form-group form-group-default float-left required">
<label for="file">File</label>
<input class="file-input" type="file" ref="file" name="file" v-
on:change="addFile($event)">
</div>
data(){
return{
films_info:
{
file: null,
}
}
},
methods: {
addFile(e){
this.films_info.file = this.$refs.file.files[0];
},
saveFilms(){
let formData = new FormData();
formData.append('file', this.films_info.file);
axios.post('/api/films/save_films',formData,{
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
location.reload(true);
})
.catch(error => {
that.errors = error.response.data.errors;
console.log("Error Here: "+error.response.data);
});
}
}
$dir_path = 'uploads/films/images';
$dir_path_resize = 'uploads/films/images/45x45';
if( $request ){
$filmsObj = new Film();
if (!File::exists($dir_path))
{
File::makeDirectory($dir_path, 0775, true);
}
if (!File::exists($dir_path_resize))
{
File::makeDirectory($dir_path_resize, 0775, true);
}
if ( $request->file ) {
$file = $request->file;
$extension = $file->getClientOriginalExtension(); // getting image extension
$file_name = 'films_'.\Carbon\Carbon::now().'.'.$extension; // renaming image
$file->move($dir_path, $file_name); // uploading file to given path
// Start : Image Resize
$image = Image::make(public_path($dir_path.'/'.$file_name));
// resize the image to a height of 45 and constrain aspect ratio (auto width)
$image->resize(null, 45, function ($constraint) {
$constraint->aspectRatio();
});
$image->save(public_path($dir_path_resize.'/'.$file_name));
// End : Image Resize
}

Resources