Call to undefined method Person::id() - laravel

Something really weird is happening, when I don't upload any image in my form it doesn't throw any error, but when I do it throws this: Call to undefined method App\Models\User_Management\Person::id()
Front-End Code
<template>
<div>
<div class="container-2 mx-8 mt-10">
<v-row>
<v-col xs="12" sm="12" md="8">
<v-card>
<v-form enctype="multipart/form-data">
<div class="container-2 mx-2">
<!-- Personal information -->
<v-divider></v-divider>
<p class="mt-2">Personal information</p>
<v-row>
<v-col cols="12" sm="12" md="4">
<v-text-field v-model="person.first_name" counter="150" :rules="id="first_name" name="first_name" label="First Name" color="black"></v-text-field>
</v-col>
<v-col cols="12" sm="12" md="4">
<v-text-field label="Last Name" v-model="person.last_name" counter="150" name="last_name" color="black"></v-text-field>
</v-col>
</v-row>
<v-divider></v-divider>
<p class="mt-2">Profile Picture</p>
<v-row>
<v-col cols="12" sm="12" md="6">
<input type="file" id="avatar" ref="avatar" v-on:change="fileUpload()"/>
</v-col>
</v-row>
<div class="right-align">
<v-btn color="yellow" class="black-text" #click="update()">Submit</v-btn>
<v-btn color="red accent-3" class="black-text">Back</v-btn>
</div>
<br>
</div>
</v-form>
</v-card>
</v-col>
</v-row>
</div>
</div>
</template>
<script>
export default {
data() {
return {
//Data
person: {
first_name: '',
last_name: '',
},
userAvatar: undefined,
person_id: 1,
}
}
},
methods: {
fileUpload(){
this.userAvatar = this.$refs.avatar.files[0];
},
update() {
let formData = new FormData();
formData.append('first_name', this.person.first_name);
formData.append('last_name', this.person.last_name);
formData.append('profile_picture', this.userAvatar);
formData.append('_method', 'PUT');
axios.post(`/profile/${this.person_id}`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(res => {
console.log(res.data);
}).catch(e => {
console.log(e);
})
},
}
}
}
</script>
Back-End Code
public function update(Request $request, $id)
{
//Person
$person = Person::find($id);
//Avatar
$name = null;
$image = null;
if ($request->hasFile('profile_picture')){
$file = $request->file('image');
$name = $person->id();
$file->move(public_path().'/img/uploads/avatars', $name);
//Image
$image = new Image();
$image->path = $name;
$image->save();
}
//Profile
$person->first_name = $request->first_name;
$person->last_name = $request->last_name;
if ($image)
$person->image_id = $image->id;
$person->save();
}
My import for the Model is written like this
use App\Models\User_Management\Person;
For some reason it seems like it doesn't recognize the ::find($id) method when I upload an image but I have no idea why, is there any fix for this? I tried to log the $request variable and it's throwing the following
[2020-04-13 19:41:23] local.INFO: array (
'first_name' => 'John',
'last_name' => 'Smith',
'_method' => 'PUT',
'profile_picture' =>
Illuminate\Http\UploadedFile::__set_state(array(
'test' => false,
'originalName' => 'kekas.jpg',
'mimeType' => 'image/jpeg',
'error' => 0,
'hashName' => NULL,
)),
)
It doesn't seem like there's something wrong with the front-end code but I left it here just in case it gives a hint about this weird error.

There's no method $person->id(); in Person Instanse. You need do get a property like this: $person->id. Hope it helps.
You're error in this code line: $name = $person->id();

Related

GraphQL mutation on Form Click not triggering

I am trying to call Apollo Mutation Hook on form submit to create a new record in the database.
export default function NewJobForm() {
const { loading, error, data } = useQuery(GET_LOC_CAT_TYPE_QUERY);
const [newJob] = useMutation(NEW_JOB_MUTATION);
const [remote, setRemote] = useState(false);
const editorRef = useRef(null);
const log = () => {
if (editorRef.current) {
console.log(editorRef.current.getContent());
}
};
let initialFormValues = {
companyName: '',
title: '',
};
const JobSchema = yup.object({
companyName: yup.string().required('Please enter the company name.'),
title: yup.string().required('Please enter the job title.'),
});
let formValidate = (values) => {
console.log(values);
const errors = {};
return errors;
};
let handleError = (field, actions) => {
field.forEach((data) => {
let field = data.param;
switch (field) {
// case 'name':
// actions.setFieldError('name', data.msg);
// break;
default:
break;
}
});
};
let formSubmit = async (values, actions) => {
console.log(values);
newJob({ variables: values });
};
return (
<>
<Formik
validationSchema={JobSchema}
validate={(values) => formValidate(values)}
onSubmit={(values, actions) => formSubmit(values, actions)}
enableReinitialize={true}
initialValues={initialFormValues}
validateOnChange={true}
validateOnBlur={false}
>
{({ handleSubmit, setFieldValue, values }) => (
<form noValidate onSubmit={handleSubmit}>
<div className="space-y-8 divide-y divide-gray-200">
<div>
<div>
<h3 className="text-lg font-medium leading-6 text-gray-900">
Job Details
</h3>
<p className="mt-1 text-sm text-gray-500">
This information will be displayed
publicly so be careful what you share.
</p>
</div>
<div className="grid grid-cols-1 mt-6 gap-y-6 gap-x-4 sm:grid-cols-6">
<div className="sm:col-span-4">
<TextInput
label="Company Name"
id="companyName"
name="companyName"
type="text"
placeholder="eg: Google"
helpText="Please Enter the name of the company"
/>
</div>
<div className="sm:col-span-4">
<TextInput
label="Job Title"
id="title"
name="title"
type="text"
/>
</div>
</div>
</div>
</div>
</form>
)}
</Formik>
</>
);
}
On Form Submit the console.log displays the message but the mutation network call does not seem to get triggered.

Cannot save multiple Image using Form Data

First of all this is the error that I get.
This is my first time working with Multiple Image with other Inputs.
as you can see in my FormData Headers the picture File is an Empty Array.
But inside the console it returns a file.
my question for this one that is this good? because the picture that I'm sending is a PNG one but it only says FILE.
This is my Vue Code.
<q-dialog
v-model="uploadForm"
transition-show="slide-up"
transition-hide="slide-down"
persistent
>
<q-card style="width: 700px; max-width: 50vw;">
<q-card-section>
<div class="text-h6">Add Product</div>
</q-card-section>
<div class="q-pa-md">
<form
action="http://127.0.0.1:8000/api/createProduct"
class="q-gutter-md"
method="POST"
enctype="multipart/form-data"
>
<q-input outlined label="Product name" v-model="data.productName" />
<q-input
multiple="multiple"
outlined
type="file"
accept="image/png,jpg/jpeg"
#change="onFilePicked"
ref="file"
>
<template v-slot:prepend>
<q-icon name="attach_file" />
</template>
</q-input>
<div>
<div class="image-category">
<div v-for="(image, key) in data.picture" :key="key">
<div class="image-item">
<img
class="grid"
:src="image.src"
width="300"
height="300"
/>
</div>
</div>
</div>
</div>
<q-input outlined label="Description" v-model="data.description" />
<q-input
prefix="₱ "
type="number"
outlined
label="Price"
v-model="data.price"
/>
<q-select
square
outlined
v-model="data.category_id"
:options="categories"
:option-value="'id'"
:option-label="'categoryName'"
label="Category"
/>
<div class="q-ma-md float-right">
<q-btn label="Submit" color="primary" #click="createProduct" />
<q-btn
label="Cancel"
color="primary"
flat
class="q-ml-sm"
#click="closeCreateModal"
/>
</div>
</form>
</div> </q-card
></q-dialog>
My returning Data()
data: {
picture: [],
productName: "Grizzly Bear",
description: "Machaba.",
price: "260000",
category_id: []
},
my #Change in accepting multiple Images.
onFilePicked(e) {
let selectedFiles = e.target.files;
for (let i = 0; i < selectedFiles.length; i++) {
let img = {
src: URL.createObjectURL(selectedFiles[i]),
file: selectedFiles[i]
};
this.data.picture.push(img);
console.log(this.data.picture, "inside");
}
console.log(this.data.picture, "outside");
},
My CreatingProduct method.
createProduct() {
let t = this.data;
if (
t.picture == "" ||
t.productName.trim() == "" ||
t.description.trim() == "" ||
t.price == "" ||
t.categories == ""
) {
this.uploadForm = false;
this.$q
.dialog({
title: "Incomplete Details",
message: "Please fill up all the fields in the Product",
persistent: true,
color: "negative"
})
.onOk(() => {
this.uploadForm = true;
});
} else {
let formData = new FormData();
const picture = JSON.stringify(this.data.picture); // returns [object, object] so I needed to do
//this.
const category_id = JSON.stringify(this.data.category_id);
formData.append("picture", picture);
formData.append("productName", this.data.productName);
formData.append("description", this.data.description);
formData.append("price", this.data.price);
formData.append("category_id", category_id);
let config = {
headers: {
"Content-Type": "multipart/form-data"
}
};
this.$axios
.post("http://127.0.0.1:8000/api/createProduct", formData, config)
.then(response => {
this.products.unshift(response.data);
this.uploadForm = false;
(this.data = ""),
this.$q.notify({
icon: "info",
message: "Product Added Successfully",
color: "positive"
});
})
.catch(() => {
this.$q.notify({
color: "negative",
position: "top",
message: "Unable to save Product",
icon: "report_problem"
});
});
}
}},
my Backend in laravel
public function createProduct(Request $request)
{
$this->validate($request,[
'productName' => 'required',
'description' => 'required',
'picture' => 'required|image|mimes:jpg,png,jpeg|max:2048',
'price' => 'required',
'category_id' => 'required'
]);
$product = new Product($request->input());
if($request->hasFile('picture')){
foreach($request->file('picture') as $file){
$name = $file->getClientOriginalName();
$file->move(public_path().'/uploads/',$name);
$product->picture = $name;
}
}
$product->save();
return response()->json($product);
}
Can anyone explain to me what is wrong? I made my research and it doesn't work. and also how do you edit a multiple Image? How would you select a specific image in the Index to make that one being edit.
this is my first time working with Quasar + Laravel in creating a Product with Multiple Image.
I think you are doing some things wrong, if you use a framework try to use it as it was intended either the validations or components they have, I detail an example
new Vue({
el: '#q-app',
data: function() {
return {
uploadForm: true,
data: {
picture: [],
productName: "Grizzly Bear",
description: "Machaba.",
price: "260000",
category_id: null
},
categories: [
{ id: 1, categoryName: 'categoria 1' }
]
}
},
methods: {
createProduct() {
this.$q.loading.show()
let formData = new FormData()
formData.append("picture", this.data.picture);
formData.append("productName", this.data.productName);
formData.append("description", this.data.description);
formData.append("price", this.data.price);
formData.append("category_id", this.data.category_id.id);
axios.post('http://localhost:8000/', formData, {
'headers': {
'Content-Type': 'multipart/form-data;'
}
}).then((response) => {
alert(response)
}).catch(error => {
if (error) {
console.log('error', error)
}
}).finally(() => {
this.$q.loading.hide()
})
},
closeCreateModal() {
alert('close')
},
}
})
<link rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/#quasar/extras/material-icons/material-icons.css">
<link rel="stylesheet"
type="text/css"
href="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.min.css">
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://cdn.jsdelivr.net/npm/quasar/dist/quasar.umd.min.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<div id="q-app">
<q-dialog v-model="uploadForm"
transition-show="slide-up"
transition-hide="slide-down"
persistent>
<q-card style="width: 700px; max-width: 50vw;">
<q-card-section>
<div class="text-h6">Add Product</div>
</q-card-section>
<q-card-section class="q-pt-none">
<div class="q-pa-md">
<q-form #submit="createProduct"
class="q-gutter-md">
<q-input outlined
label="Product name"
v-model="data.productName"
:rules="[val => !!val || 'Field is required']"></q-input>
<q-file multiple outlined v-model="data.picture" label="Outlined" accept="image/png,jpg/jpeg"
:rules="[val => !!val || 'Field is required']">
<template v-slot:prepend>
<q-icon name="attach_file" ></q-icon>
</template>
</q-file>
<q-input outlined
label="Description"
v-model="data.description"
:rules="[val => !!val || 'Field is required']"></q-input>
<q-input prefix="₱ "
type="number"
outlined
label="Price"
v-model="data.price"
:rules="[val => !!val || 'Field is required']"></q-input>
<q-select square
outlined
v-model="data.category_id"
:options="categories"
option-value="id"
option-label="categoryName"
label="Category"
:rules="[val => !!val || 'Field is required']"
></q-select>
<div>
<q-btn label="Submit"
color="primary"
type="submit"></q-btn>
<q-btn label="Cancel"
#click="closeCreateModal"
flat class="q-ml-sm"></q-btn>
</div>
</q-form>
</div>
</q-card-section>
</q-card>
</q-dialog>
</div>
if you see the log the "picture" attribute has an array of Object File
I leave the backend processing (laravel) at your discretion
I think it would be easier if you share a link with your problem for future reference
https://jsfiddle.net/idkc/L8xn0puq/52/

Vee Validate - Sever side validation and front end validation

I have a laravel lumen endpoint which i return errors from. The issue i am facing is that vee validate is not setting the errors?
<template>
<v-container fluid class="ma-0 pa-0">
<v-card route>
<v-card-title class="primary white--text">Create New User</v-card-title>
<v-row align="center" :class="{'px-6': $vuetify.breakpoint.xs, 'pa-8': $vuetify.breakpoint.smAndUp}">
<v-row justify="space-between" v-if="!isCreating">
<v-col cols="12" md="4">
<v-row align="center" justify="center" class="mb-4">
<v-avatar color="primary" size="128">
<v-icon size="48" dark v-if="!userAvatarName()">mdi-account</v-icon>
<span
v-if="userAvatarName()"
class="white--text display-2"
>{{ userAvatarName() }}</span>
</v-avatar>
</v-row>
</v-col>
<v-col>
<ValidationObserver v-slot="{ invalid, validated, passes }" ref="provider">
<v-form #submit.prevent="passes(handleSubmit)">
<v-row >
<v-col class="py-0">
<VTextFieldWithValidation vid="username" rules="required" v-model="newUser['username']" label="Username" />
</v-col>
</v-row>
<v-row >
<v-col class="py-0">
<VTextFieldWithValidation vid="email" rules="required|email" v-model="newUser['email']" label="Email address" />
</v-col>
</v-row>
<v-row >
<v-col class="py-0">
<VTextFieldWithValidation rules="required" v-model="newUser['first_name']" label="First name" />
</v-col>
<v-col class="py-0">
<VTextFieldWithValidation rules="required" v-model="newUser['last_name']" label="Last name" />
</v-col>
</v-row>
<v-row >
<v-col class="py-0 mt-4">
<ValidationProvider name="role" rules="required" v-slot="{ errors, valid }">
<v-select
:error-messages="errors"
:success="valid"
:items="roles"
item-text="name" item-value="value"
label="Role"
outlined
v-model="newUser['role']"
></v-select>
</ValidationProvider>
</v-col>
</v-row>
<v-divider></v-divider>
<v-row >
<v-col class="py-0">
<v-switch
v-model="newUser['send_activation']"
label="Send Activation Email"
></v-switch>
</v-col>
</v-row>
<v-row v-if="!newUser['send_activation']">
<v-col class="py-0">
<ValidationProvider name="password" :rules="!newUser['send_activation'] ? 'required|min:8' : ''" v-slot="{ errors, valid }">
<v-text-field
outlined
counter
:error-messages="errors"
:success="valid"
label="Password"
:append-icon="userPasswordVisibility.password_current_show ? 'mdi-eye' : 'mdi-eye-off'"
:type="userPasswordVisibility.password_current_show ? 'text' : 'password'"
#click:append="userPasswordVisibility.password_current_show = !userPasswordVisibility.password_current_show"
v-model="newUser['password']"
></v-text-field>
</ValidationProvider>
</v-col>
<v-col class="py-0">
<ValidationProvider name="confirmation" :rules="!newUser['send_activation'] ? 'required|password:#password' : ''" v-slot="{ errors, valid }">
<v-text-field
outlined
counter
:error-messages="errors"
:success="valid"
label="Password Confirmation"
:append-icon="userPasswordVisibility.password_new_show ? 'mdi-eye' : 'mdi-eye-off'"
:type="userPasswordVisibility.password_new_show ? 'text' : 'password'"
#click:append="userPasswordVisibility.password_new_show = !userPasswordVisibility.password_new_show"
v-model="newUser['password_confirmation']"
></v-text-field>
</ValidationProvider>
</v-col>
</v-row>
<v-row class="mt-4">
<v-col class="py-0">
<v-btn text color="primary" :to="{ name: 'organisation/users', params: { org_id: organisation.id }}">Cancel</v-btn>
</v-col>
<v-col class="py-0 text-right">
<v-tooltip top>
<template v-slot:activator="{ on }">
<v-btn outlined color="primary" v-on="on" #click="passes(handleSubmit)" :disabled="invalid || !validated">Add New User</v-btn>
</template>
<span>Create new user and add them to <strong>{{organisation.name}}</strong>.</span>
</v-tooltip>
</v-col>
</v-row>
</v-form>
</ValidationObserver>
</v-col>
</v-row>
<v-row align="center" justify="center" v-if="isCreating">
<v-progress-circular
class="ma-5"
indeterminate
color="teal accent-1 darken-2" >
</v-progress-circular>
</v-row>
</v-row>
</v-card>
</v-container>
</template>
<script>
import { mapState } from 'vuex'
import { CREATE_USER } from '#/_apollo/GraphQL/userGraphQL'
import BreadcrumbsManager from '#/_util/breadcrumbManager'
import VTextFieldWithValidation from '#/components/inputs/VTextFieldWithValidation'
import { ValidationProvider, ValidationObserver } from "vee-validate"
export default {
name: 'CreateUser',
mixins: [BreadcrumbsManager],
props: [
'organisation'
],
components: {
ValidationObserver,
ValidationProvider,
VTextFieldWithValidation
},
data() {
return {
userPasswordVisibility: {
password_current_show: false,
password_new_show: false,
password_new_confirm_show: false
},
newUser: {
send_activation: 1
},
roles: [
{ name: 'Admin', value: 'organisation_admin' },
{ name: 'User', value: 'user' }
],
isCreating: false
}
},
computed: {
...mapState({
authUser: state => state.AUTH_STORE.authUser
}),
},
methods: {
userAvatarName() {
let name = '';
if (this.newUser['first_name']) {
name += `${this.newUser['first_name'].charAt(0).toUpperCase()}`
}
if (this.newUser['last_name']) {
name += `${this.newUser['last_name'].charAt(0).toUpperCase()}`
}
if (name !== '') {
return name
}
return false
},
async handleSubmit() {
this.isCreating = true
this.newUser['reseller_id'] = this.authUser.reseller_id
this.newUser['organisation_id'] = parseInt(this.$route.params.org_id)
this.newUser['send_activation'] = this.newUser['send_activation'] ? 1 : 0
var refs = this.$refs.provider
this.$apollo
.mutate({
mutation: CREATE_USER,
variables: this.newUser
})
.then(response => {
this.isCreating = false
const user = response.data.createUser
this.$store.commit('USER_STORE/CREATE_USER', response.data.createUser)
this.$toast.success('Successfully created new user.')
this.$router.push({ name: "organisation/users", params: { org_id: this.$route.params.org_id }}, () => {})
}).catch((error) => {
this.isCreating = false
this.$toast.error(error.graphQLErrors[0].extensions.code.message)
let errors = error.graphQLErrors[0].extensions.code.errors
console.log(errors)
refs.setErrors(errors)
console.log(refs)
})
refs.validate();
}
},
created() {
this.setBreadcrumbs([
{ text: 'Dashboard' , path: '/' },
{ text: 'Organisations' , path: '/organisation/all/' },
{ text: ':organisation' },
{ text: 'Users', path: `/organisation/${this.organisation.org_id}/users/` },
{ text: 'Create' }
])
this.replaceBreadcrumb({
find: ':organisation',
replace: { text: this.organisation.name, path: `/organisation/${this.organisation.org_id}` }
})
}
}
</script>
In the catch error on handle submit i can see the sever errors but my view wont show an error on the field?
I have took inspiration from the following from the docs:
https://codesandbox.io/s/veevalidate-backend-driven-validation-ynrp9?from-embed=&file=/src/App.vue
But to no avail.
Can anyone help?
It turns out apollo client is a promise which isnt resolved so the fix is:
const userResponse = await this.$apollo
.mutate({
mutation: CREATE_USER,
variables: this.newUser
})
.then(response => {
this.isCreating = false
const user = response.data.createUser
this.$store.commit('USER_STORE/CREATE_USER', response.data.createUser)
this.$toast.success('Successfully created new user.')
this.$router.push({ name: "organisation/users", params: { org_id: this.$route.params.org_id }}, () => {})
}).catch((error) => {
this.isCreating = false
this.$toast.error(error.graphQLErrors[0].extensions.code.message)
return error.graphQLErrors[0].extensions.code.errors.detail
})
this.$refs.provider.setErrors(userResponse);

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 = '';
}
}
})

Upload Image in Vue Component with Laravel

I am making a simple website that has a feature to upload images. I tried it Laravel way which I made it in blade template and it works fine. Now I am trying to make it inside Vue Components
Here's my Create.vue
<template>
<div>
<div class="row">
<input type="hidden" name="_token" :value="csrf">
<div class="col-md-5">
<div class="detail-container">
<label for="title">Book Title:</label>
<input type="text" name="title" id="title" v-model="book_title" class="form-control">
</div>
<div class="detail-container">
<label for="title">Book Description:</label>
<textarea type="text" name="description" id="description" v-model="book_description" class="form-control" rows="5"></textarea>
</div>
<div class="detail-container">
<label for="title">Tags:</label>
<multiselect v-model="tags" :show-labels="false" name="selected_tags" :hide-selected="true" tag-placeholder="Add this as new tag" placeholder="Search or add a tag" label="name" track-by="id" :options="tagsObject" :multiple="true" :taggable="true" #tag="addTag" #input="selectTags">
<template slot="selection" slot-scope="tags"></template>
</multiselect>
</div>
</div>
<div class="col-md-7">
<!-- BOOK COVER WILL GO HERE -->
<div class="detail-container">
<label>Book Cover:</label>
<input type="file" class="form-control-file" id="book_cover" name="selected_cover" #change="onFileChange">
<small id="fileHelp" class="form-text text-muted">After you select your desired cover, it will show the preview of the photo below.</small>
<div id="preview">
<img v-if="url" :src="url" height="281" width="180" />
</div>
</div>
</div>
<div class="detail-container" style="margin-top: 20px;">
<button class="btn btn-primary" #click="saveBook()">Next</button>
</div>
</div>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
// register globally
Vue.component('multiselect', Multiselect)
export default {
// OR register locally
components: { Multiselect },
data () {
return {
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
url: null,
selected_cover: null,
tags: [],
tagsObject: [],
selected_tags: [],
book_title: '',
book_description: ''
}
},
methods: {
getTags() {
let vm = this;
axios.get('/admin/getTags').then(function(result){
let data = result.data;
for(let i in data) {
vm.tagsObject.push({id: data[i].id, name: data[i].name});
}
});
},
addTag (newTag) {
const tag = {
name: newTag,
id: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
}
this.tagsObject.push(tag);
this.tags.push(tag);
},
selectTags(value) {
this.selected_tags = value.map(a=>a.id);
},
onFileChange(e) {
const file = e.target.files[0];
this.url = URL.createObjectURL(file);
this.selected_cover = file;
},
saveBook() {
const fd = new FormData();
fd.append('image', this.selected_cover, this.selected_cover.name)
console.log(this.selected_cover);
var book_details = {
'title': this.book_title,
'description': this.book_description,
'book_cover': this.selected_cover,
'tags': this.selected_tags
};
axios.post('/admin/saveBook', book_details).then(function(result){
console.log('done')
})
}
},
created() {
this.getTags();
}
}
</script>
<!-- New step!
Add Multiselect CSS. Can be added as a static asset or inside a component. -->
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
and here's my controller
public function store(Request $request)
{
$this->validate(request(), [
'title' => 'required|min:5',
'description' => 'required|min:10',
'book_cover' => 'required|image|mimes:jpeg,jpg,png|max:10000'
]);
// File Upload
if($request->hasFile('book_cover')) {
$fileNameWithExt = $request->file('book_cover')->getClientOriginalName();
// GET FILE NAME
$filename = pathinfo($fileNameWithExt, PATHINFO_FILENAME);
// GET EXTENSION
$extension = $request->file('book_cover')->getClientOriginalExtension();
// File Unique Name
$fileNameToStore = $filename. '_'. time().'.'.$extension;
$path = $request->file('book_cover')->storeAs('public/book_covers', $fileNameToStore);
} else {
$fileNameToStore = 'noimage.jpg';
}
$book = new Book;
$book->title = request('title');
$book->description = request('description');
$book->book_cover = $fileNameToStore;
$book->save();
$book->tags()->sync($request->tags, false);
return back()->with('success', 'Book Created Successfully!');
}
I never touched my controller because this is what I used when I do this feature in Laravel Way but when I save it, the details are being saved but the image is not uploading instead it saves noimage.jpg in the database. Does anyone know what I am doing wrong?
i tried to add this part const fd = new FormData(); in book_details but when i console.log(fd) it returned no data.
you should pass the fd object and not the book_details in your POST request.
you could do it something like this.
onFileChange(e) {
const file = e.target.files[0];
// this.url = URL.createObjectURL(file);
this.selected_cover = file;
},
saveBook() {
const fd = new FormData();
fd.append('image', this.selected_cover)
fd.append('title', this.book_title)
fd.append('description', this.book_description)
fd.append('book_cover', URL.createObjectURL(this.selected_cover))
fd.append('tags', this.selected_tags)
axios.post('/admin/saveBook', fd).then(function(result){
console.log('done')
})
}
and also, you can't just console.log the fd in the console. what you can do instead is something like this
for (var pair of fd.entries()) {
console.log(pair[0]+ ', ' + pair[1]);
}
FormData is a special type of object which is not stringifyable and cannot just be printed out using console.log. (link)

Resources