Cyber Monday! Get 25% off for as long as you keep your subscription. New subscribers only.

Adam_Commit

Adam_Commit

Member Since 6 Months Ago

Experience Points
24,690
Total
Experience

310 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
237
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 5
24,690 XP
Nov
09
3 weeks ago
Activity icon

Started a new Conversation Earning XP Points And Badges

Hi everyone,

Looking for some advice/direction of approach rather than how to implement firstly. So have been watching and following the "Unlocking badges" series, which is great, but it jumps in at a level where users are already earning points/XP.

Before I delve any further into that series, I think it is important to map out the best structure for earning XP (points) for various task within a site before moving on to the unlocking of achievements.

There are lots of contradictory views/opinions (as always) on whether to just had a column to the user table, or whether to have a separate experience table with a 1-to-1 relationship etc, so given, I will be looking to expand this, over time, to unlock badges etc but if I want to show users, where they have earnt points etc, then I am guessing I may need both an experience table that records the activity that took place along with the amount of points assigned for it AND a column on the database which records the overall points total?

Then possible create an event and listeners to create the records in the DB for each activity and subsequently update the total_points col on users.

Or am I just over-complicating things?

I am just struggling to get an idea of starting points here, I am a freelance/solo dev so don't have the luxury of banding ideas around with anyone else, unfortunately.

I believe once I have a robust XP approach in place, then I can move onto badges. :)

If anyone has any experience, or resources which I can look at or just general best tips with regards this then that would be great.

Oct
28
1 month ago
Activity icon

Started a new Conversation Redis / Homestead / Laravel 7 - Connection Refused....

Hey,

I am trying to get Redis working whilst following the Let's build a forum with TDD series. I have got to Episode 66 which introduces Redis and written the first test...and then boom, it blows up. Have done a lot of googling around but the answers do not seem to correlate.

  1. I am using homestead and thus redis should be installed in the environment - using a Vagrant box on Windows.

  2. I have installed predis and confirm this is pulling in via the vendor library and compose package

  3. I have SSH'd into the homestead box and run redis-cli and performed ping /pong test to confirm that the redis server is infact running.

  4. I have also run a redis-server --version version test.

Redis server v=5.0.8 sha=00000000:0 malloc=jemalloc-5.1.0 bits=64 build=129cf1a0751f12a

Following the tutorial I have written the first test

    public function test_it_increments_a_threads_score_each_time_it_is_read()
    {

        $this->assertEmpty( Redis::zrevrange('trending_threads', 0, -1));

        $thread = create('App\Thread');

        $this->call('GET', $thread->path());

        Redis::zrevrange('trending_threads', 0, -1);

        $this->assertCount(1,  Redis::zrevrange('trending_threads', 0, -1));


    }

and all I get is

Predis\Connection\ConnectionException : No connection could be made because the target machine actively refused it. [tcp://127.0.0.1:6379]

I am really struggling to work out why/how this message is persisting when I have followed everything line by line, I am not using Xamp or anything other than Homestead yet still getting this error.

The config is the standard config, updated to the predis library

    'redis' => [

        'client' => env('REDIS_CLIENT', 'predis'),

        'options' => [
            'cluster' => env('REDIS_CLUSTER', 'redis'),
            'prefix' => env('REDIS_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_database_'),
        ],

        'default' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', 'localhost'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_DB', '0'),
        ],

        'cache' => [
            'url' => env('REDIS_URL'),
            'host' => env('REDIS_HOST', '127.0.0.1'),
            'password' => env('REDIS_PASSWORD', null),
            'port' => env('REDIS_PORT', '6379'),
            'database' => env('REDIS_CACHE_DB', '1'),
        ],

    ],

Has anybody come across this before or have any idea on direction? It is driving me bonkers and is quite a big blocker in the series.

For reference - I am running Laravel 7 on Homestead via a Vagrant Box on my windows laptop. :)

Cheers all

Oct
26
1 month ago
Activity icon

Replied to :assertCount() Must Be A Countable Or Iterable - Laravel 7

Cheers @tykus - To confirm - conflict between user table column naming conversion and the notification collection/relationship on the user model - changing the notification column flag to allow_notifications changed the dump from an integer to a collection - and therefore, was countable, thus allowing me to use the assertCount.

Thanks

Adam

Activity icon

Replied to :assertCount() Must Be A Countable Or Iterable - Laravel 7

Hmmmm, I think it could be a conflict looking at those specific dumps. I currently have a field of the user model called notifications that is a boolean 1/0 column that I use to flag whether a user has accepted to receive notifications in the registration.

Do you think it is possible that the test is actually conflicting with that field name as the DD on

dd(auth()->user()->notifications);

Return Int 1 - which I believe is referring to the col value on the user table as opposed to the notification collection?

I will try modifying the name of that column and run the tests again.

Activity icon

Replied to :assertCount() Must Be A Countable Or Iterable - Laravel 7

Thanks @tykus it's defined using the notifiable trait pretty much out of the box as per the docs, and called via ->notify method

Activity icon

Started a new Conversation :assertCount() Must Be A Countable Or Iterable - Laravel 7

Hey everyone,

Been trying to battle away with the Lets build a Forum with TDD, and I am about half way through, so far, its going OK, but been stuck for a while now on why I can't get my tests to pass.

Basically - all test are passing apart from this one:

    public function test_a_notification_is_prepared_when_a_subscribed_thread_receives_a_new_reply_that_is_not_by_current_user()
    {

        $thread = create('App\Thread')->subscribe();

        $this->assertCount(0, auth()->user()->notifications);

        $thread->addReply([
            'user_id' => auth()->id(),
            'body' => 'Some reply here'
        ]);

        $this->assertCount(0, auth()->user()->fresh()->notifications);

        $thread->addReply([
            'user_id' => create('App\User')->id,
            'body' => 'Some reply here'
        ]);

        $this->assertCount(1, auth()->user()->fresh()->notifications);

    }

With the error in the test being

Argument #2 of PHPUnit\Framework\Assert::assertCount() must be a countable or iterable

I have commented out all 3 instances of this, and tried to debug it, but can't work it out. All other test are passing, I have also tried to look at the Github repo for the corresponding episodes and my code is almost identical - in fact I tried the code from the repo to double check I was being silly.

Has anyone come across this issue? The episodes are around 44-46 of the series "Lets build a forum with TDD"

I am building on Laravel 7 with PHP 7.4. The PHP unit version is 8.5.8.

I am not bothered at this stage if the test does not pass, I just want to change the error message!

Cheers

Oct
14
1 month ago
Activity icon

Replied to Let's Build A Forum With TTD - Laravel 7

@sinnbeck Thank you and agreed. I love Laravel and the Laracast community. I've already had a lot of fantastic support. It really is brilliant!

Activity icon

Replied to Let's Build A Forum With TTD - Laravel 7

Think it's a go-er then. Thanks both :) @michaloravec @tray2

Activity icon

Started a new Conversation Let's Build A Forum With TTD - Laravel 7

Just wondered if anyone has done the in-depth Laravel Forum Laracasts series with Laravel 7.

I am keen to start it, but scrolling through the video is what designed for Laravel 5.4 then half way through refactored to 5.5.

As it's such an in-depth and long course and will require a lot of commitment, I don't want to start it only to realise it's far removed from Laravel 7 and it's approach and that I can't actually follow it.

Any thoughts on whether it's a go-er?

Cheers

Oct
09
1 month ago
Activity icon

Replied to URL Encoding - To Preg Replace Or Not

Thank you @sinnbeck is their any advatange/disadvantage to this, I would prefer to decode to clean the URI but wan't sure if there was a reason that the query builder didn't decode by default and thus is their a reason not to decode. :)

Activity icon

Started a new Conversation URL Encoding - To Preg Replace Or Not

After receiving some great tips and advice from the community, I wanted to ask peoples view on whether to leave URL parameters encoded (but less visually appealing) or to pre_replace to tidy them up.

I am not adverse to either, but just wondered whether their was any disadvatage/advatgaes or security related concerns - or is it just best to keep the parameter encoded as per the query builder

Current:

mysite.com/posts?category%5B%5D=garden&tags%5B%5D=shrubs

Current preg_replace to "tidy the URL" to remove the index within the array but to keep the URL encoded

$query = preg_replace('/%5B[0-9]+%5D/', '%5B%5D', $query);

Possible preg_replace to give a "Best vanity" / clean URL

$query = preg_replace('/%5B[0-9]+%5D/', '[]', $query);

This version would produce URLs like

mysite.com/posts?category[]=garden&tags[]=shrubs

Does anybody have any thoughts on tidying the URLs or does exposing the array as square bracket params open up security issues?

Cheers

Oct
08
1 month ago
Activity icon

Replied to Removing An Associated Key/value Pair From Array (nested)

@automica also I believe I've set up the relationships correctly. I have tags and category models and pivot tables to associate to the post :)

Activity icon

Replied to Removing An Associated Key/value Pair From Array (nested)

@automica your help both last post and this one has been invaluable to me. And I must thank you for taking the time out time point me in the right direction. It really humbles me the help I've received from you. :) I hope to be able to repay the favour ! The Laravel and Laracast community continue to impress!

Activity icon

Awarded Best Reply on Laravel Policies - 2 Possible Conditions

UPDATE: Working solution

Basically, I was querying two models separately and trying to pass them both as parameters.

As there was already a relationship set up between Projects and Comments, and subsequently already a relationship set up between Projects and Users, I could grab these value IDs and query them against the current User ID. So (and as intended by laravel, hence my original problem) I don't need to try to access project model.

Update to comment policy:

    //Commenter (user who made comment) or Author of project can delete
    public function delete(User $user, Comment $comment){

        // User is commenter OR User is the original author of the post.
        return $comment->user_id === $user->id || $comment->project->user_id === $user->id;

    }

Hopefully may be of use to others if they stumble upon trying to authorize to different user types.

Cheers

Activity icon

Started a new Conversation Removing An Associated Key/value Pair From Array (nested)

I have two function to add remove parameters to the query string. The "add_query_params" (thanks to this forum) is working nicely and I can now add multiple tags to the query string of the same type.

For example

http://mysite.com?tags[]=flowers&tags[]=shrubs&category[]=garden

As you can see, I can add multiple of the same parameters, I am also querying these nicely using queryfilters.

However my newest problem, is simply removing a single tag type without affecting the rest of the query string. I will then rebuild the query without the deleted tag.

Someone kindly yesterday helped me to to a point but this removes ALL tag key values, not just the specified tag.

So if I was to delete say $tags[]shrubs from the above URL it would actually delete BOTH tag[]shrubs AND $tags[]flowers.

This obviously isn't very intuitive for a filter system I am devising. What I would like to know is how to remove just the single key value pair and leave the other keys pairs intact.

Here is my helper function

//Accept a param array which passthrough through tag type eg category/tag and value
function remove_query_params(array $params = [])
{
    //Set to array
    $existingParams = [];

    $existingParams = request()->query();

    foreach($params as $key=>$value){

        if (isset($existingParams[$value])) {
            unset($existingParams[$value]);
        }
    }


    $query = http_build_query($existingParams);

    return url()->current() . '?' . $query;
}

//Need to return: user removes tag from filter in blade, URL recontructs without the passed through tag value
//Before
//http://mysite.com?tags[]=flowers&tags[]=shrubs&category[]=garden

//After
//http://mysite.com?tags[]=flowers&category[]=garden

This does not work, if I change $value to $key then it will will, but it will remove all keys of the same type, not the behaviour I would like.

I activate this behaviour via a call in the blade template, this forms a href

//Pass through parameter type and parameter value
{{remove_query_params(['category' => $category->id]) }}

Has anybody got any pointers as to where I go next?#

Thanks and fingers crossed I am not far off :)

Adam

Oct
07
1 month ago
Activity icon

Replied to Query Strings Aand Parameters - Blowing My Mind!

@automica Thank you, this is kind of where I thought I needed to head, but this is a real point in the right direction from an implementation point of view. Thanks for the help.

Activity icon

Replied to Query Strings Aand Parameters - Blowing My Mind!

Thank you very much @automica , This makes perfect sense in terms of needing an array, I suppose my problem here is how would I go about constructing this using my functions, as currently, it does not append the query string with two parameters, but it strips the old one out at replaces it - eg url.com?tags=plants to url.com?tags=house

If I can work out how to push these values to the URL then I think I am onto a winner. Many thanks for your help!

Activity icon

Replied to Query Strings Aand Parameters - Blowing My Mind!

Thank you very much @rodrigo.pedra, This makes perfect sense in terms of needing an array, I suppose my problem here is how would I go about constructing this using my functions, as currently, it does not append the query string with two parameters, but it strips the old one out at replaces it - eg url.com?tags=plants to url.com?tags=house

If I can work out how to push these values to the URL then I think I am onto a winner. Many thanks for your help!

Activity icon

Started a new Conversation Query Strings Aand Parameters - Blowing My Mind!

Hi community,

Running into a brick wall here with Query String/Parameter building, I have a feeling it shouldn't be this complicated but wanted to get best practice advice.

I am currently building a community platform that allows/requires users to tag their content. The content is tagged via two query string parameters.

Please note all relationships, tagging logic for posts including attaching/detaching is done, this is purely a filter system issue.

  1. Category - posts must have a category but only one category, on searching the query string would be appended as such. Eg. ?category=plants

  2. Tags - posts can have (optionally) many tags, this would append to the query string as such. Eg. ?tags=hardy

So far. I have created two functions (through a helper.php file) that "add_query_params" and "remove_query_params". These are helpful as it allows me to add and remove either ?category or ?tag without removing the other. MY PROBLEM is that I can not for the life of me seem to work out how to add MULTIPLE of the same tag to the query string to allow me to filter on MULTIPLE parameter options!

For example

I can build

website.com?category=plants&tags=hardy 

// I can also remove either tag without affecting the other

I cannot build

.com?category=plants&tags=hardy&tags=perenial
// (I can't add two of the same tag to the query string to allow me to filter on these in the controller/request

My functions that allow me to add/remove tags are as follows

/**
 * URL before:
 * https://example.com/orders/123?order=ABC009&status=shipped
 *
 * 1. remove_query_params(['status'])
 * 2. remove_query_params(['status', 'order'])
 *
 * URL after:
 * 1. https://example.com/orders/123?order=ABC009
 * 2. https://example.com/orders/123
 */
function remove_query_params(array $params = [])
{
    $url = url()->current(); // get the base URL - everything to the left of the "?"
    $query = request()->query(); // get the query parameters (what follows the "?")

    foreach($params as $param) {
        unset($query[$param]); // loop through the array of parameters we wish to remove and unset the parameter from the query array
    }

    return $query ? $url . '?' . http_build_query($query) : $url; // rebuild the URL with the remaining parameters, don't append the "?" if there aren't any query parameters left
}

/**
 * URL before:
 * https://example.com/orders/123?order=ABC009
 *
 * 1. add_query_params(['status' => 'shipped'])
 * 2. add_query_params(['status' => 'shipped', 'coupon' => 'CCC2019'])
 *
 * URL after:
 * 1. https://example.com/orders/123?order=ABC009&status=shipped
 * 2. https://example.com/orders/123?order=ABC009&status=shipped&coupon=CCC2019
 */
function add_query_params(array $params = [])
{

    $query = array_merge(
        request()->query(),
        $params
    ); // merge the existing query parameters with the ones we want to add


    return url()->current() . '?' . http_build_query($query); // rebuild the URL with the new parameters array
}


And in the blade comment they would be called like so

//Add query param
<a href="{{add_query_params(['tags' => $tag->id]) }}"
                class="inline-block bg-gray-100  px-3 py-1 text-xs uppercase font-button">
                #{{$tag->name}}
</a>


//Remove query param
<a href="{{remove_query_params(['category']) }}"
                class="inline-block bg-gray-300  px-3 py-1 text-xs uppercase font-button">
                #{{$has_category->name}}
                            
 </a>

It's driving me bonkers, most sites, e-commerce, travel sites allow you to update the query string and this approach I felt was the easiest to be called from anywhere in the site and agnostic from being tied down to a particular page, function, it also makes the builder extensible and reusable for more tags.

But I just can't work it out.

Anybody have any tips or direction on how to add multiple tags to the query string????

Cheers

Jul
27
4 months ago
Activity icon

Replied to Laravel Policies - 2 Possible Conditions

UPDATE: Working solution

Basically, I was querying two models separately and trying to pass them both as parameters.

As there was already a relationship set up between Projects and Comments, and subsequently already a relationship set up between Projects and Users, I could grab these value IDs and query them against the current User ID. So (and as intended by laravel, hence my original problem) I don't need to try to access project model.

Update to comment policy:

    //Commenter (user who made comment) or Author of project can delete
    public function delete(User $user, Comment $comment){

        // User is commenter OR User is the original author of the post.
        return $comment->user_id === $user->id || $comment->project->user_id === $user->id;

    }

Hopefully may be of use to others if they stumble upon trying to authorize to different user types.

Cheers

Activity icon

Started a new Conversation Laravel Policies - 2 Possible Conditions

Hi community,

I have been racking me brain over this one, that seems like it should be terrribly easy.

I am building a system that allows commenting, I have built this out in laravel and vue js.

So far the CRUD actions are all working etc. My issue is with regards policies for authorized actions.

What I am trying to achieve is simply - both the commeter (the user who comments on a specific post) and author (of that specific post) both have permission/authorization to delete the comment.

I have a CommentPolicy (and had it working for just the owner of the comment to delete, this currently returned 403 if not the owner) but I can't seem to add-in permission for the project owner to also delete - which is an important attribute, the type of policy will be well used within the site so want to get it right.

CommentPolicy.php

<?php

namespace App\Policies;

use App\Comment;
use App\Project;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class CommentPolicy
{
    use HandlesAuthorization;

    //Does the authenticated user own the comment (only commenter can udpate)
    public function update(User $user, Comment $comment){
        return $comment->user_id === $user->id;
    }
    //Commenter (user who made comment) or Author of project can delete
    public function delete(User $user, Project $project, Comment $comment){
        return $user->is($comment->project->owner) || $comment->user_id === $user->id;
    }
}

CommentController.php (very simple - specifically looking here at the destroy method at the bottom)

<?php

namespace App\Http\Controllers;

use App\Comment;
use App\Project;
use Illuminate\Http\Request;

class CommentController extends Controller
{
    public function index()
    {
        $comments = Comment::with('user')
            ->orderByDesc('id')
            ->get();
        return response($comments, 200);
    }

    public function show(Project $project)
    {
        $comments = $project->comments;

        return response($comments, 200);
    }

    public function store(Request $request)
    {

        //dd($request->all());

        $data = $request->validate([
            'body' => 'required|string',
            'projectID' => 'required'
        ]);

        $comment = auth()->user()
            ->comments()
            ->create([
                'body' => $data['body'],
                'project_id' => $data['projectID']
            ]);


        $comment->load('user');

        return response($comment, 200);
    }

    public function update(Request $request, Comment $comment)
    {
        //Authorise use can update
        $this->authorize('update', $comment);


        $data = $request->validate([
            'body' => 'required|string'
        ]);
        $comment->body = $data['body'];
        $comment->save();
        $comment->load('user');
        return response($comment, 200);
    }

    public function destroy(Comment $comment, Project $project)
    {
        //Authorise use can delete
        $this->authorize('delete', $comment);


        $comment->delete();
        return response( null,204);
    }
}

When I try to add this policy - it returns only 403 even for the comment creator, so I am struggling how to work out, how to query these two specific conditions.

I do also have a ProjectsPolicy.php but I am not sure calling the authorize method twice seems correct.

For reference, the ProjectPolicy.php (used for editing projects by author)

<?php

namespace App\Policies;

use App\Project;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class ProjectPolicy
{
    use HandlesAuthorization;

    //Does the authenticated user own the project
    public function update(User $user, Project $project){

        return $project->user_id === $user->id;

    }
}

Any pointers in the right directions would be amazing, policies seem the right way to go and super powerful :)

Cheers

Jul
07
4 months ago
Activity icon

Replied to Vue JS Edit Modal - Pass Current Data By Post ID

Thank you Bryan. Really helpful. :)

Activity icon

Replied to Edit A Project Modal - Vue JS + Laravel (Birdboard Course)

This is a great point in the right direction.

Thank you, I will certainly look at that series in more detail!

Activity icon

Started a new Conversation Vue JS Edit Modal - Pass Current Data By Post ID

I could just be being very dumb here, but I've hit a brick wall.

Been following along both the Tweety (Laravel from scratch) and Birdboard (Build a Laravel app with TDD) - Both great tutorials.

However, I am quite new to VueJs - and need to advice/direction on how to pass current "project" data to the form for editing.

Currently the "Add new project" feature - is a vue component that contains a modal with a new project form in. This is all working as expected (accessed via the index page).

I am now looking to convert my current /projects{id}/edit route (that has a standard blade and HTML format) to a vue form (to reduce repetition and create some DRY code) similar to my new project form.

I have searched hi and low but can't seems to find any resource that covers this.

How can I pass the current data to the edit form to populate the fields with the data already associated with that project?

I would like the form to be accessible via the edit route or the projects index page (via modal).

Here is my code run down::

EditProject.vue (basically the same as "newProject.vue at the moment)

<template>
    <modal name="edit-project" classes="p-10 bg-card rounded-lg" height="auto">
        <h1 class="font-normal mb-16 text-center text-2xl">Let's start something new</h1>
        <form @submit.prevent="submit">
            <div class="flex">
                <div class="flex-1 mr-4">
                    <div class="mb-4">
                        <label for="title" class="text-sm block mb-2">Title</label>
                        <input
                            type="text"
                            id="title"
                            class="border border-muted-light py-2 px-2 block rounded w-full"
                            :class="form.errors.title ? 'border-red-800' : 'border-muted-light'"
                            v-model="form.title"
                        >
                        <span class="text-xs italic text-red-800" v-if="form.errors.title" v-text="form.errors.title[0]"></span>
                    </div>

                    <div class="mb-4">
                        <label for="description" class="text-sm block mb-2">Description</label>
                        <textarea
                            type="text"
                            id="description"
                            class="border border-muted-light py-2 px-2 block rounded w-full"
                            row="7"
                            :class="form.errors.body ? 'border-red-800' : 'border-muted-light'"
                            v-model="form.body"
                        ></textarea>
                        <span class="text-xs italic text-red-800" v-if="form.errors.body" v-text="form.errors.description[0]"></span>

                    </div>
                </div>
                <div class="flex-1 ml-4">
                    <div class="mb-4">
                        <label class="text-sm block mb-2">Need some tasks?</label>
                        <input
                            type="text"
                            class="border border-muted-light mb-2 py-2 px-2 block w-full"
                            placeholder="Task 1"
                            v-for="task in form.tasks"
                            v-model="task.body">
                    </div>

                    <button type="button" class="inline-flex items-center text-sm" @click="addTask">
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 496" class="w-full mr-2" style="max-width:18px;">
                            <g>
                                <path d="M248,0C111.033,0,0,111.033,0,248c0.154,136.903,111.097,247.846,248,248c136.967,0,248-111.033,248-248S384.967,0,248,0
				z M248,480C119.87,480,16,376.13,16,248C16.146,119.93,119.93,16.146,248,16c128.13,0,232,103.87,232,232S376.13,480,248,480z"/>
                                <path d="M400,240H256V96c0-4.418-3.582-8-8-8s-8,3.582-8,8v144H96c-4.418,0-8,3.582-8,8s3.582,8,8,8h144v144c0,4.418,3.582,8,8,8
				s8-3.582,8-8V256h144c4.418,0,8-3.582,8-8S404.418,240,400,240z"/>
                            </g>
                        </svg>
                        <span>Add new task</span>
                    </button>

                </div>
            </div>

            <footer class="flex justify-end">
                <button type="button" class="button is-outlined mr-4" @click="$modal.hide('edit-project')">Cancel</button>
                <button type="submit" class="button">Update project</button>
            </footer>

        </form>
    </modal>
</template>

<script>
    //Import Form logic
    import PostForm from "./postForm";

    export default {
	//I assume somewhere here I need to find the current post id and pass it into the data?
        data() {
            return {
                form: new PostForm({
                    title: '',
                    body: '',
                    tasks: [
                        { body: ''},
                    ]
                })
            };
        },
        methods: {
            addTask() {
                this.form.tasks.push({ body: '' });
            },
            async submit() {
                //Remove tasks from submission if empty as will error out
                if (! this.form.tasks[0].body){
                    delete this.form.originalData.tasks;
                }
		//Will update this to patch but need to work out how to pass invidual post id to route
                this.form.submit('/projects')
                    .then(response => location = response.data.message);
            }
        }
    }
</script>

My form import class

class PostForm {
    constructor(data) {

        this.originalData = JSON.parse(JSON.stringify(data));

        Object.assign(this, data);

        this.errors = {};
        this.submitted = false;

    }

    data() {
        let data = {};

        for (let attribute in this.originalData){
            data[attribute] = this[attribute];
        }

        return data;

    }

    post(endpoint){
        this.submit(endpoint);
    }

    patch(endpoint){
        this.submit(endpoint, 'patch');
    }

    delete(endpoint){
        this.submit(endpoint, 'delete');
    }

    submit(endpoint, requestType =  'post') {
        return axios[requestType](endpoint, this.data())
            .catch(this.onFail.bind(this))
            .then(this.onSuccess.bind(this));
    }

    onSuccess(response){
        this.submitted = true;
        this.errors = {};
        return response;
    }

    onFail(error){
        this.errors = error.response.data.errors;
        this.submitted = false;

        throw error;
    }

    reset(){
        Object.assign(this, this.originalData);
    }
}

export default PostForm;

My edit.blade.php (currently in modal - but happy for this form to live on page if helps to refactor

@extends('layouts.app')

@section('content')


    <div class="container">
        <div class="row">
            <div class="col-md-6">
                <h1 class="font-normal mb-16 text-center text-2xl">
                    Edit {{ $project->title }}

                </h1>
                <a class="button"
                   href="/projects/{{$project->id}}/edit"
                   @click.prevent="$modal.show('edit-project')"
                >
                    edit project
                </a>

            </div>
            <div class="col-md-6 projects-container" style="height: 35rem; overflow-y: scroll">
                <edit-project></edit-project>
            </div>
        </div>
    </div>


@endsection

I have spent two days trying to work how to pass data to the form and get it working like the standard blade edit approach where you set the values using the data sent through the controller (like this, old example):

        <label
            class="label text-sm mb-2 block"
            for="title"
        >
            Title
        </label>

        <input
            class="input bg-transparent border border-gray-400 rounded p-2 text-sm w-full"
            type="text"
            name="title"
            id="title"
            placeholder="Title"
            value="{{ $project->title  }}"

        >

        @error('title')
        <p class="text-red-500 text-xs mt-2">{{ $message }}</p>
        @enderror

It surely must be do-able, am I totally over complicating it or missing something really simple? I don't want to give up on vue as seems really clean and powerful so here's hoping the community can help! :D

Cheers everyone

Activity icon

Started a new Conversation Edit A Project Modal - Vue JS + Laravel (Birdboard Course)

Hit a brick wall. Been following along both the Tweety (Laravel from scratch) and Birdboard (Build a Laravel app with TDD) - Both great tutorials.

However, I am quite new to VueJs - and need to advice/direction on how to pass current "project" data to the form for editing.

Currently the "Add new project" feature - is a vue component that contains a modal with a new project form in. This is all working as expected (accessed via the index page).

I am now looking to convert my current /projects{id}/edit route (that has a standard blade and HTML format) to a vue form (to reduce repetition and create some DRY code) similar to my new project form.

I have searched hi and low but can't seems to find any resource that covers this.

How can I pass the current data to the edit form to populate the fields with the data already associated with that project?

I would like the form to be accessible via the edit route or the projects index page (via modal).

Here is my code run down::

My project controller

<?php

namespace App\Http\Controllers;
use App\Project;
use Illuminate\Http\Request;

class ProjectsController extends Controller
{
   public function index(){


       $projects = auth()->user()->accessibleProjects();


       return view('projects.index', compact('projects'));
   }

    public function show(Project $project){


        $this->authorize('update', $project);

        //If the current user is project owner - return the project

        return view('projects.show', compact('project'));
    }

    public function create(){
        return view('projects.create');
    }

    public function store(){

        $attributes = $this->validateRequest();

        $project = auth()->user()->projects()->create($attributes);

        if (request()->has('tasks')){
            //then for each task - go to the project addTask method to create it.
            foreach (request('tasks') as $task){
                $project->addTask($task['body']);
            }
        }

        if (request()->wantsJson()) {
            return ['message' => $project->path()];
        }


        //redirect (if normal request - not js/json)
        return redirect($project->path());
    }

    //Edit a project - accepting the current project as a paramater
    public function edit(Project $project){
        //Return view to edit form with $project data passed through
        return view('projects.edit', compact('project'));
    }

    public function update(Project $project){
     
        $this->authorize('update', $project);

        //Validate the request here.
        $attributes = $this->validateRequest();

        //Otherise lets go an update the project with the validated request
        $project->update($attributes);

        //Then return to the project
        return redirect($project->path());
    }


    /**
     * Delete a project
     */
    public function destroy(Project $project){
        //Ensure terh user is authorized to delete the project
        $this->authorize('manage', $project);

        //Delete the project
        $project->delete();

        return redirect('/projects');

    }

    /**
     * @return array
     */
    protected function validateRequest()
    {
        return request()->validate([
            'title' => 'sometimes|required',
            'description' => 'sometimes|required',
            'notes' => 'nullable'
        ]);

    }
}

My editProject.vue component

<template>
    <modal name="edit-project" classes="p-10 bg-card rounded-lg" height="auto">
        <h1 class="font-normal mb-16 text-center text-2xl">Let's start something new</h1>
        <form @submit.prevent="submit">
            <div class="flex">
                <div class="flex-1 mr-4">
                    <div class="mb-4">
                        <label for="title" class="text-sm block mb-2">Title</label>
                        <input
                            type="text"
                            id="title"
                            class="border border-muted-light py-2 px-2 block rounded w-full"
                            :class="form.errors.title ? 'border-red-800' : 'border-muted-light'"
                            v-model="form.title"
                        >
                        <span class="text-xs italic text-red-800" v-if="form.errors.title" v-text="form.errors.title[0]"></span>
                    </div>

                    <div class="mb-4">
                        <label for="description" class="text-sm block mb-2">Description</label>
                        <textarea
                            type="text"
                            id="description"
                            class="border border-muted-light py-2 px-2 block rounded w-full"
                            row="7"
                            :class="form.errors.body ? 'border-red-800' : 'border-muted-light'"
                            v-model="form.body"
                        ></textarea>
                        <span class="text-xs italic text-red-800" v-if="form.errors.body" v-text="form.errors.description[0]"></span>

                    </div>
                </div>
                <div class="flex-1 ml-4">
                    <div class="mb-4">
                        <label class="text-sm block mb-2">Need some tasks?</label>
                        <input
                            type="text"
                            class="border border-muted-light mb-2 py-2 px-2 block w-full"
                            placeholder="Task 1"
                            v-for="task in form.tasks"
                            v-model="task.body">
                    </div>

                    <button type="button" class="inline-flex items-center text-sm" @click="addTask">
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 496" class="w-full mr-2" style="max-width:18px;">
                            <g>
                                <path d="M248,0C111.033,0,0,111.033,0,248c0.154,136.903,111.097,247.846,248,248c136.967,0,248-111.033,248-248S384.967,0,248,0
				z M248,480C119.87,480,16,376.13,16,248C16.146,119.93,119.93,16.146,248,16c128.13,0,232,103.87,232,232S376.13,480,248,480z"/>
                                <path d="M400,240H256V96c0-4.418-3.582-8-8-8s-8,3.582-8,8v144H96c-4.418,0-8,3.582-8,8s3.582,8,8,8h144v144c0,4.418,3.582,8,8,8
				s8-3.582,8-8V256h144c4.418,0,8-3.582,8-8S404.418,240,400,240z"/>
                            </g>
                        </svg>
                        <span>Add new task</span>
                    </button>

                </div>
            </div>

            <footer class="flex justify-end">
                <button type="button" class="button is-outlined mr-4" @click="$modal.hide('edit-project')">Cancel</button>
                <button type="submit" class="button">Update project</button>
            </footer>

        </form>
    </modal>
</template>

<script>
    //Import Form logic
    import PostForm from "./postForm";

    export default {
	//Guessing I somehow now to grab the current data from the current post ID??//
        data() {
            return {
                form: new PostForm({
                    title: '',
                    body: '',
                    tasks: [
                        { body: ''},
                    ]
                })
            };
        },
        methods: {
            addTask() {
                this.form.tasks.push({ body: '' });
            },
            async submit() {
                //Remove tasks from submission if empty as will error out
                if (! this.form.tasks[0].body){
                    delete this.form.originalData.tasks;
                }
		//Will update this to the patch route once I know a bit more about passing the data through
                this.form.submit('/projects')
                    .then(response => location = response.data.message);
            }
        }
    }
</script>

My postForm.js (that we import as I believe, a global form helper)

class PostForm {
    constructor(data) {

        this.originalData = JSON.parse(JSON.stringify(data));

        Object.assign(this, data);

        this.errors = {};
        this.submitted = false;

    }

    data() {
        let data = {};

        for (let attribute in this.originalData){
            data[attribute] = this[attribute];
        }

        return data;

    }

    post(endpoint){
        this.submit(endpoint);
    }

    patch(endpoint){
        this.submit(endpoint, 'patch');
    }

    delete(endpoint){
        this.submit(endpoint, 'delete');
    }

    submit(endpoint, requestType =  'post') {
        return axios[requestType](endpoint, this.data())
            .catch(this.onFail.bind(this))
            .then(this.onSuccess.bind(this));
    }

    onSuccess(response){
        this.submitted = true;
        this.errors = {};
        return response;
    }

    onFail(error){
        this.errors = error.response.data.errors;
        this.submitted = false;

        throw error;
    }

    reset(){
        Object.assign(this, this.originalData);
    }
}

export default PostForm;

edit.blade.php

@extends('layouts.app')

@section('content')


    <div class="container">
        <div class="row">
            <div class="col-md-6">
                <h1 class="font-normal mb-16 text-center text-2xl">
                    Edit {{ $project->title }}

                </h1>
                <a class="button"
                   href="/projects/{{$project->id}}/edit"
                   @click.prevent="$modal.show('edit-project')"
                >
                    edit project
                </a>

            </div>
            <div class="col-md-6 projects-container" style="height: 35rem; overflow-y: scroll">
                <edit-project></edit-project>
            </div>
        </div>
    </div>


@endsection

I have spent two full days trying to work how to pass data to the form and get it working like the standard blade edit approach where you set the values using the data sent through the controller (like this, old example):

        <label
            class="label text-sm mb-2 block"
            for="title"
        >
            Title
        </label>

        <input
            class="input bg-transparent border border-gray-400 rounded p-2 text-sm w-full"
            type="text"
            name="title"
            id="title"
            placeholder="Title"
            value="{{ $project->title  }}"

        >

        @error('title')
        <p class="text-red-500 text-xs mt-2">{{ $message }}</p>
        @enderror

It surely must be do-able, am I totally over complicating it or missing something really simple, I don't want to give up on vue as seems really clean and powerful so here's hoping the community can help! :D

Cheers everyone