pazitron

pazitron

Member Since 4 Years Ago

Experience Points
44,790
Total
Experience

210 experience to go until the next level!

In case you were wondering, you earn Laracasts experience when you:

  • Complete a lesson — 100pts
  • Create a forum thread — 50pts
  • Reply to a thread — 10pts
  • Leave a reply that is liked — 50pts
  • Receive a "Best Reply" award — 500pts
Lessons Completed
428
Lessons
Completed
Best Reply Awards
0
Best Reply
Awards
  • start your engines Created with Sketch.

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • first-thousand Created with Sketch.

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • 1-year Created with Sketch.

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • 2-years Created with Sketch.

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • 3-years Created with Sketch.

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • 4-years Created with Sketch.

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • 5-years Created with Sketch.

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • school-in-session Created with Sketch.

    School In Session

    Earned when at least one Laracasts series has been fully completed.

  • welcome-newcomer Created with Sketch.

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • full-time-student Created with Sketch.

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • pay-it-forward Created with Sketch.

    Pay It Forward

    Earned once you receive your first "Best Reply" award on the Laracasts forum.

  • subscriber Created with Sketch.

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • lifer Created with Sketch.

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • evangelist Created with Sketch.

    Laracasts Evangelist

    Earned if you share a link to Laracasts on social media. Please email [email protected] with your username and post URL to be awarded this badge.

  • chatty-cathy Created with Sketch.

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • lara-veteran Created with Sketch.

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • 10k-strong Created with Sketch.

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • lara-master Created with Sketch.

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • laracasts-tutor Created with Sketch.

    Laracasts Tutor

    Earned once your "Best Reply" award count is 100 or more.

  • laracasts-sensei Created with Sketch.

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • top-50 Created with Sketch.

    Top 50

    Earned once your experience points ranks in the top 50 of all Laracasts users.

Level 9
44,790 XP
Aug
06
2 months ago
Activity icon

Replied to File Upload Fails Using Vue.js Laravel

thank you all for your help. I've learned a lot about uploading images using vue and storing them in Laravel. After reading your helpful responses and searching on the web, the following worked for me:

Vue components:

FileUploder.vue

<template>
    <label
        class="w-48 flex flex-col items-center px-2 py-4 bg-indigo-100 text-gray-500 rounded tracking-wide border-1 border-indigo-200 cursor-pointer hover:bg-indigo-600 hover:text-gray-200">
          <svg class="w-5 h-5" fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
              <path d="M16.88 9.1A4 4 0 0 1 16 17H5a5 5 0 0 1-1-9.9V7a3 3 0 0 1 4.52-2.59A4.98 4.98 0 0 1 17 8c0 .38-.04.74-.12 1.1zM11 11h3l-4-4-4 4h3v3h2v-3z" />
          </svg>
          <span class="mt-2 leading-tight text-xs">Add company logo</span>
          <input type='file' @change="onChange" class="hidden">
    </label>
</template>

<script>
    export default {
        methods: {
            onChange(e) {
                if (! e.target.files.length) return;
                let file = e.target.files[0];
                let reader = new FileReader();
                reader.readAsDataURL(file);
                reader.onload = e => {
                    let src = e.target.result;
                    this.$emit('loaded', { src, file });
                };
            }
        }
    }
</script>

JobCreate.vue

<template>
    <section>
        <form @submit.prevent="createJob" enctype="multipart/form-data">
            <div class="bg-white shadow overflow-hidden sm:rounded-lg">
              <div class="px-4 py-5 border-b border-indigo-100 sm:px-6">
                <h3 class="text-lg leading-6 font-medium text-gray-900">
                  New job form
                </h3>
                <p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500">
                  <sup>*</sup>Required fields
                </p>
              </div>
              <div>
                <dl>
                      <div class="bg-gray-50 px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Job title<sup>*</sup>
                        </dt>
                        <dd class="mt-1 sm:mt-0 sm:col-span-2">

                            <input
                                v-model="title"
                                type="text"
                                placeholder="Email Developer"
                                aria-label="Job title"
                                class="transition-colors duration-100 ease-in-out focus:outline-0 text-base border border-indigo-200 focus:bg-white focus:outline-none focus:border-indigo-300 text-gray-800 rounded-lg bg-white md:rounded-lg py-2 pr-4 pl-4 block w-full appearance-none leading-normal">
                        </dd>
                      </div>
                      <div class="bg-white px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Company<sup>*</sup>
                        </dt>
                        <dd class="mt-1 sm:mt-0 sm:col-span-2">

                            <input
                                v-model="company"
                                type="text"
                                placeholder="Acme Ltd."
                                aria-label="Company name"
                                class="transition-colors duration-100 ease-in-out focus:outline-0 text-base border border-indigo-200 focus:bg-white focus:outline-none focus:border-indigo-300 text-gray-800 rounded-lg bg-white md:rounded-lg py-2 pr-4 pl-4 block w-full appearance-none leading-normal">
                        </dd>
                      </div>
                      <div class="bg-white px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Company logo
                        </dt>
                        <dd class="mt-1 sm:mt-0 sm:col-span-2">

                            <file-uploader @loaded="onLoad"></file-uploader>
                        </dd>
                      </div>
                      <div class="bg-white px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Location<sup>*</sup>
                        </dt>
                        <dd class="mt-1 sm:mt-0 sm:col-span-2">

                            <input
                                v-model="location"
                                type="text"
                                placeholder="Solihull, UK"
                                aria-label="job location"
                                class="transition-colors duration-100 ease-in-out focus:outline-0 text-base border border-indigo-200 focus:bg-white focus:outline-none focus:border-indigo-300 text-gray-800 rounded-lg bg-white py-2 pr-4 pl-4 block w-full appearance-none leading-normal">
                        </dd>
                      </div>
                      <div class="bg-gray-50 px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Type<sup>*</sup>
                        </dt>
                        <dd class="mt-1 text-sm sm:mt-0 sm:col-span-2">
                            <select
                                v-model="type"
                                aria-label="Job category"
                                class="border text-base border-indigo-200 bg-transparent text-gray-500 leading-normal max-w-sm focus:bg-white focus:outline-none focus:border-indigo-300 rounded-lg bg-white py-2 pr-4 pl-4 block">
                                <option selected disabled value=''>Select type</option>
                                <option>Full-time</option>
                                <option>Part-time</option>
                                <option>Contract</option>
                                <option>Other</option>
                            </select>
                        </dd>
                      </div>
                      <div class="bg-gray-50 px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Category<sup>*</sup>
                        </dt>
                        <dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">

                            <select
                                v-model="categoryId"
                                aria-label="Job category"
                                class="border border-indigo-200 bg-transparent text-gray-500 text-base leading-normal max-w-sm focus:bg-white focus:outline-none focus:border-indigo-300 bg-white rounded-lg py-2 pr-4 pl-4 block">
                                <option selected disabled value=''>Select category</option>
                                <option
                                    v-for="(category, id) in categories"
                                    :value="category.id">
                                {{category.name}}
                                </option>
                            </select>
                        </dd>
                      </div>
                      <div class="bg-white px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Salary (recommended)
                        </dt>
                        <dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
                            <input
                                v-model="salary"
                                type="text"
                                placeholder="e.g. £50,000"
                                aria-label="Salary"
                                class="transition-colors duration-100 text-base ease-in-out focus:outline-0 border border-indigo-200 focus:bg-white focus:outline-none focus:border-indigo-300 text-gray-800 rounded-lg bg-white md:rounded-lg py-2 pr-4 pl-4 appearance-none leading-normal">
                        </dd>
                      </div>
                      <div class="bg-gray-50 px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Job description<sup>*</sup>
                        </dt>
                        <dd class="mt-1 sm:mt-0 sm:col-span-2">
                            <wysiwyg
                                v-model="jobDescription"
                                name="job description">
                            </wysiwyg>
                        </dd>
                    </div>
                    <div class="bg-white px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Link to apply<sup>*</sup>
                        </dt>
                        <dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">

                            <input
                                v-model="linkToApply"
                                type="text"
                                aria-label="Link to apply"
                                placeholder="https://www.acme.com/career/apply"
                                class="transition-colors duration-100 text-base ease-in-out focus:outline-0 border border-indigo-200 focus:bg-white focus:outline-none focus:border-indigo-300 text-gray-800 rounded-lg bg-white md:rounded-lg py-2 pr-4 pl-4 block w-full appearance-none leading-normal">
                        </dd>
                    </div>
                    <div class="bg-gray-50 px-4 py-5 items-center sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
                        <dt class="text-sm leading-5 font-medium text-gray-600">
                          Email<sup>*</sup>
                        </dt>
                        <dd class="mt-1 sm:mt-0 sm:col-span-2">

                            <input
                                v-model="email"
                                type="text"
                                placeholder="[email protected]"
                                aria-label="Email address"
                                class="transition-colors duration-100 ease-in-out focus:outline-0 text-base border border-indigo-200 focus:bg-white focus:outline-none focus:border-indigo-300 text-gray-800 rounded-lg bg-white md:rounded-lg py-2 pr-4 pl-4 block w-full appearance-none leading-normal">
                        </dd>
                    </div>
                </dl>
                </div>
                <div class="px-4 py-5 bg-gray-100 border-t border-indigo-100 sm:px-6 flex items-center justify-end">
                    <span class="block">
                        <a
                            href="/manage/jobs"
                            class="btn btn-text">
                            Cancel
                        </a>
                    </span>

                    <span class="ml-6">
                        <button
                            type="submit"
                            class="btn btn-primary btn-small">
                            Create job
                        </button>
                    </span>
                </div>
            </div>
        </form>
    </section>
</template>

<script>
    import Wysiwyg from '../../Wysiwyg';
    import FileUploader from '../../FileUploader';

    export default {
        components: {
            'wysiwyg': Wysiwyg,
            'file-uploader': FileUploader
        },
        data() {
            return {
                categories: [],
                title: '',
                company: '',
                companyLogo: '',
                uploadedFile: '',
                email: '',
                location: '',
                type: '',
                categoryId: '',
                salary: '',
                jobDescription: '',
                linkToApply: ''
            }
        },

        mounted() {
            this.fetchCategories();
        },

        methods: {
            fetchCategories() {
                axios.get('/categories')
                    .then(response => {
                      this.categories = response.data;
                    })
                    .catch(error => {
                        console.log(error);
                    })
            },

            onLoad(avatar) {
                console.log(avatar);
                this.companyLogo = avatar.src;
                this.uploadedFile = avatar.file;
            },

            createJob() {
                let data = new FormData();
                data.append('company_logo', this.uploadedFile);
                data.append('title', this.title);
                data.append('description', this.jobDescription);
                data.append('company', this.company);
                data.append('location', this.location);
                data.append('salary', this.salary);
                data.append('link_to_apply', this.linkToApply);
                data.append('type', this.type);
                data.append('category_id', this.categoryId);
                data.append('email', this.email);

                axios.post('/manage/jobs', data)
                .then(response => {
                    console.log(response);
                })
                .catch(errors => {
                    console.log(errors.data);
                })
            }
        }
    }
</script>

Controller:

public function store(Request $request) {


        $validatedInput = $request->validate([
            'title' => 'required | max:255',
            'description' => 'required',
            'location' => 'required',
            'link_to_apply' => 'required',
            'email' => 'required | email',
            'company' => 'required',
            'category_id' => 'required'
        ]);

        if ($request->has('company_logo')) {
            $job = new Job;

            $job->title = request('title');
            $job->description = request('description');
            $job->location = request('location');
            $job->link_to_apply = request('link_to_apply');
            $job->type = request('type');
            $job->category_id = request('category_id');
            $job->access_token = Str::orderedUuid()->toString();
            $job->email = request('email');
            $job->company = request('company');
            $job->about_company = request('about_company');
            $job->salary = request('salary');
            $job->company_logo = $request->file('company_logo')->store('images/company_logos', 'public');

            $job->save();
            return 'New job and image saved';

        } else {
            $job = new Job;

            $job->title = request('title');
            $job->description = request('description');
            $job->location = request('location');
            $job->link_to_apply = request('link_to_apply');
            $job->type = request('type');
            $job->category_id = request('category_id');
            $job->access_token = Str::orderedUuid()->toString();
            $job->email = request('email');
            $job->company = request('company');
            $job->about_company = request('about_company');
            $job->salary = request('salary');

            $job->save();

            return 'New job saved, no image';
        }

Hope this helps someone who is equally as new to the file upload using Vue/Laravel as I was!

Activity icon

Replied to File Upload Fails Using Vue.js Laravel

I tried and got an error ""file_put_contents(uploads/5f2c093002d14..jpeg): failed to open stream: No such file or directory"

then I replaced File:: with Storage:: and it did work - the file was saved but it's zero kbytes

Activity icon

Replied to File Upload Fails Using Vue.js Laravel

@ajithlal yes, I am getting them as a file now. Though due to putting everything into the formData object, my validation for other fields like title, description, etc. fails. I am looking at a solution for it now

Activity icon

Replied to File Upload Fails Using Vue.js Laravel

I combined yours and @sinnbeck latest replies and I can store the image successfully - thank you for that. Though I have a different issue now.

My other field's validation fails, I suspect due to the data sitting in formData object, this is my controller:

public function store(Request $request) {

        $validatedInput = $request->validate([
            'title' => 'required | max:255',
            'description' => 'required',
            'location' => 'required',
            'link_to_apply' => 'required',
            'email' => 'required | email',
            'company' => 'required',
            'category_id' => 'required'
        ]);

        $job = new Job;

        $job->title = request('title');
        $job->description = request('description');
        $job->location = request('location');
        $job->link_to_apply = request('link_to_apply');
        $job->type = request('type');
        $job->category_id = request('category_id');
        $job->access_token = Str::orderedUuid()->toString();
        $job->email = request('email');
        $job->company = request('company');
        $job->about_company = request('about_company');
        $job->salary = request('salary');

        $job->save();

        if(request()->hasFile('company_logo')) {
            $job->update([
                'company_logo' => $request->file('company_logo')->store('images/company_logos', 'public')
            ]);

        }
        return 'Saved to DB';
    }

I tried adding adding this:

$request['data'] = json_decode($request['data']);

    // Validate
    $request->validate([
        'data.title' => 'required',
        'data.description' => 'required'
    ]);

But it did not help, I still get errors: {description: ["The description field is required."], location: ["The location field is required."],…} message: "The given data was invalid."

Activity icon

Replied to File Upload Fails Using Vue.js Laravel

It's just ignores the if statement if I use hasFile()

Aug
05
2 months ago
Activity icon

Replied to File Upload Fails Using Vue.js Laravel

I tried the two approaches from the article you posted - both resulted in the same error: "message: "Call to a member function store() on null". This is so frustrating.

Activity icon

Started a new Conversation File Upload Fails Using Vue.js Laravel

I keep getting "Call to a member function store() on null" when I use Vue to post a file and other data.

Vue.js component:

<template>
    <section>
        <form @submit.prevent="createJob" enctype="multipart/form-data">

		<input v-model="title" type="text" placeholder="title">
		<input type='file' @change="onFileSelected"/>
	
		<button type="submit" > Post </button>
	</form>
    </section>
</template>

<script>
	export default {
       
        data() {
            return {
                title: '',
                companyLogo: ''
            }
        },
methods: {
onFileSelected(e) {
                let image = e.target.files[0];
                let reader = new FileReader();
                reader.readAsDataURL(image);
                reader.onload = e => {
                    this.companyLogo = e.target.result;
                }
            },

	createJob() {
                axios.post('/manage/jobs', {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    },
                    'title': this.title,
                    'company_logo': this.companyLogo
                })
                .then(response => {
                    console.log(response);
                })
                .catch(errors => {
                    console.log(errors.data);
                })
            }
}

}

My controller:

public function store(Request $request) { 

	$job = new Job;

        $job->title = request('title');
	$job->save();

	if(request()->has('company_logo')) {
            $job->update([
                'company_logo' => $request->file('company_logo')->store('images/company_logos', 'public')
            ]);
        }

        return 'Saved to DB';

}

I think it has something to do with the front end converting the image into base64 on upload and the backend (laravel) expecting the actual file, but not sure. My store() method returns null.

exception: "Error"
file: ".../app/Http/Controllers/ManageJobsController.php"
line: 91
message: "Call to a member function store() on null"


line 91 is 'company_logo' => $request->file('company_logo')->store('images/company_logos', 'public')
Jul
03
3 months ago
Activity icon

Replied to Best Way To Implement Expires In 30 Days

@martinbean I ended up using scopes, this is how I implemented the "less than 30 days" constraint:

public function scopeActive($query)
    {
        $expirationDate = Carbon::today()->subDays(30);
        return $query->where('created_at', '>=', $expirationDate);
    }
Activity icon

Replied to Best Way To Implement Expires In 30 Days

Thank you @bugsysha. Not sure how to use that in my situation. I can see from the docs how it can be applied to temporary routes, for example limiting the unsubscribe link in emails sent from Laravel.

On the back end I just need something to look at the created_at column and if it's more than 30 days - omit it from the index. Whether it's a simple query builder or scope like suggested by @martinbean - I am trying to figure out all the options.

Activity icon

Replied to Best Way To Implement Expires In 30 Days

thank you @martinbean. I have never worked with scopes before. Found this in the docs https://laravel.com/docs/7.x/eloquent#query-scopes

Any learning material you can recommend about scopes in laravel (I should have probably checked laracasts before asking you :D)?

Activity icon

Started a new Conversation Best Way To Implement Expires In 30 Days

Hi there, I am building a job board where you can add a job listing for 30 days. Any listing will be "active" for 30 days and will become "expired" on the 31st day and therefore will be hidden from the front end, job index.

I am looking for suggestion on how to implement this. I am using Laravel 6 and Vue.js

Thank you

May
11
5 months ago
Activity icon

Started a new Conversation How To Redirect To Subdomain On Successful Authentication?

I am trying to implement multi tenancy where each registered user will have their own subdomain -e.g. user_subdomain.example.com

My users authenticate on example.com - how can I redirect them to their subdomain on successful login/registration? - For example user_subdomain.example.com/dashboard. My users table will have the subdomain field.

May
05
5 months ago
Activity icon

Commented on Rapid Code Generation With Blueprint

Amazing tool, thank you Jason!