Corbin

Corbin

Member Since 5 Years Ago

Experience Points
35,410
Total
Experience

4,590 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
359
Lessons
Completed
Best Reply Awards
1
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 8
35,410 XP
Jun
21
3 weeks ago
Activity icon

Started a new Conversation Custom Exception Handler For API Is Not Working

I'm having problems catching Exceptions in my Laravel/Sanctum/ SPA/API that uses Cahsier. I can catch Stripe exceptions in the handler method, but can't catch NotFoundHttpException.

In my controller I'm deliberately setting a query parameter that I know will fail to test the exception:

Controller

public function processCheckout(Request $request)
{    
    
    $plan = Plan::findOrFail(100);
    $user = $user = auth()->user();
    
    //If user has no subscriptions subscribe them to new plan
    if($user->subscriptions->count() === 0){
        $user->newSubscription($plan->name, $plan->stripe_plan_id)->create($request->payment_method['id']);

        //return response([], 201);
    }
}

When I turn on: APP_DEBUG=true I get this response:

{
    "message": "No query results for model [App\Plan] 100",
    "exception": "Symfony\Component\HttpKernel\Exception\NotFoundHttpException",
    "file": "/Applications/MAMP/htdocs/saas-spa/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php",
    "line": 222,
    "trace": [
        {
            "file": "/Applications/MAMP/htdocs/saas-spa/vendor/laravel/framework/src/Illuminate/Foundation/Exceptions/Handler.php",
            "line": 198,
            "function": "prepareException",
            "class": "Illuminate\Foundation\Exceptions\Handler",
            "type": "->"
        },
        {
            "file": "/Applications/MAMP/htdocs/saas-spa/app/Exceptions/Handler.php",
            "line": 85,
            "function": "render",
            "class": "Illuminate\Foundation\Exceptions\Handler",
            "type": "->"
        }, // and more
	]
}

When I turn APP_DEBUG=false I get nothing as a response.

app/Exceptions/Handler.php

public function render($request, Throwable $exception)
{
    if ($exception instanceof \Stripe\Exception\ApiErrorException) {
        return response()->json([
            'code' => $exception->getCode(),
            'message' => $exception->getMessage(),
        ]);
    }
    $response = $this->handleException($request, $exception);
    return $response;
}

public function handleException($request, Throwable $exception)
{

    if ($exception instanceof MethodNotAllowedHttpException) {
        return $this->errorResponse('The specified method for the request is invalid', 405);
    }

    if ($exception instanceof NotFoundHttpException) {
        return $this->errorResponse('The specified URL cannot be found', 404);
    }

    if ($exception instanceof HttpException) {
        return $this->errorResponse($exception->getMessage(), $exception->getStatusCode());
    }

    if (config('app.debug')) {
        return parent::render($request, $exception);            
    }
    return $this->errorResponse('Unexpected Exception. Try later', 500);

}

Why am I not able to output my custom error message?

Jun
17
3 weeks ago
Activity icon

Started a new Conversation Error Handling For Cashier Controller Methods While Building An API

Essentially I have a CheckoutController that handles the functionality for subscribing, unsubscribing, resuming a subscription, and changing a subscription. For example:

public function processCheckout(Request $request)
{
    
    try {
        $plan = Plan::findOrFail($request->billing_plan_id);
        $user = $user = auth()->user();
        
        //If user has no subscriptions subscribe them to new plan
        if($user->subscriptions->count() === 0){
            $user->newSubscription($plan->name, $plan->stripe_plan_id)->create($request->payment_method['id']);

            return response([], 201);
        }
    } catch (\Exception $e) {
        return response([], 500);
    }
}

What I don't understand is what type of errors I should output for the frontend to pick up if something goes wrong. Right now for all of my Checkout methods I just out put a 500 error, and on the front end I catch that error by outputting a message: "An error occurred. Please try again."

submitPaymentForm(paymentMethod){
        this.$http({
            method: 'POST',
            url: 'api/checkout',
            data: {
                billing_plan_id: this.planId,
                payment_method: paymentMethod
            }
        }).then(function (response) {
            this.$store.dispatch('getUser')
        }.bind(this))
        .catch(error => {
                this.serverErrorMessage = "An error occurred. Please try again.";
                console.log('submit payment'+error);
        });
    },
},

Now, I do understand that Stripe implements its own errors on the frontend, but I'm not sure what to do with my backend. I was hoping someone could help me out with this.

Here's my full gist of the functionality

Jun
03
1 month ago
Activity icon

Started a new Conversation Checking To See If User Has Subscriptions Causes Test To Fail Unexpectedly [Laravel Cashier]

I've created a method that processes user checkouts using cashier:

CheckoutController.php (working)

public function processCheckout(Request $request)
{
    $plan = Plan::findOrFail($request->billing_plan_id);

    try {
        $user = auth()->user()->newSubscription($plan->name, $plan->stripe_plan_id)->create($request->payment_method['id']);

        return response([], 201);
    } catch (\Exception $e) {
        return response([], 500);
    }
}

SubscriptionsTest

use RefreshDatabase;

protected $monthlyPlan;
protected $yearlyPlan;

protected function setUp(): void
{
    parent::setUp();

    $this->monthlyPlan = factory('App\Plan')->create(['name' => 'Monthly', 'stripe_plan_id' => 'plan_thing']); 
    $this->yearlyPlan = factory('App\Plan')->create(['name' => 'Yearly', 'stripe_plan_id' => 'plan_thing2']);      
}

/** @test */
public function a_user_can_subscribe_to_a_plan()
{
    $this->withoutExceptionHandling();

    $this->actingAs($user = factory('App\User')->create());

    \Stripe\Stripe::setApiKey(\Config::get('services.stripe.secret'));
    $payment_method = \Stripe\PaymentMethod::create([
        'type' => 'card',
        'card' => [
            'number' => '4242424242424242',
            'exp_month' => 5,
            'exp_year' => 2021,
            'cvc' => '314',
        ],
    ]);

    $response = $this->json('POST', '/api/checkout', ['billing_plan_id'=> 1, 'payment_method' => $payment_method ] )
        ->assertStatus(201);

    $this->assertTrue($user->subscribed('Monthly'));
}

Everything works great until I need to check to see if a user is already subscribed, and if they aren't create a subscription auth()->user()->newSubscription(). This is to prevent duplicate subscriptions.

CheckoutController (Not working)

public function processCheckout(Request $request)
{
    
    try {
        $plan = Plan::findOrFail($request->billing_plan_id);
        $user = $user = auth()->user();
        
        //If user has no subscriptions subscribe them to new plan
        if($user->subscriptions->count() === 0){

            $user->newSubscription($plan->name, $plan->stripe_plan_id)->create($request->payment_method['id']);

            //dd($user->subscribed('Monthly'));

            return response([], 201);
        }
    } catch (\Exception $e) {
        return response([], 500);
    }
}

Test:

Tests\Feature\SubscriptionsPlansTest::a_user_can_subscribe_to_a_plan Failed asserting that false is true.

As you can see I've //dd($user->subscribed('Monthly')); in the controller method. This returns false. dd($user->subscriptions->toArray()); also returns an empty array.

I'm unsure what is going on here and why dd($user->subscribed('Monthly')); is returning false. I'm pretty sure this works because the front end is working. What's going on?

May
29
1 month ago
Activity icon

Replied to Cookie::queue() Not Creating A Cookie For API Login

@bugsysha I had no idea it existed. Is there the possibility for two factor authentication?

Activity icon

Started a new Conversation Cookie::queue() Not Creating A Cookie For API Login

I'm using passport with Laravel 7 to login in and store a refresh token in a cookie:

class AuthController extends Controller
{
    const REFRESH_TOKEN = 'refreshToken';

    public function login(Request $request)
    {

        $request->validate([
            'username' => 'required|email',
            'password' => 'required',
        ]);

        return $this->proxy('password', [
            'username' => $request->username,
            'password' => $request->password,
        ]);

    }

    public function refresh(Request $request)
    {
        $refreshToken = $this->request->cookie(self::REFRESH_TOKEN);

        return $this->proxy('refresh_token', [
            'refresh_token' => $refreshToken
        ]);
    }

    public function proxy($grantType, array $data = [])
    {
        $data = array_merge($data, [
            'client_id'     => config('services.passport.client_id'),
            'client_secret' => config('services.passport.client_secret'),
            'grant_type'    => $grantType
        ]);

        $response = Request::create(route('passport.token'), 'POST', $data);

        $handleResponse = app()->handle($response);

        $data = json_decode($handleResponse->getContent());

        // Create a refresh token cookie
        Cookie::queue(
            self::REFRESH_TOKEN,
            $data->refresh_token,
            864000, // 10 days
            null,
            null,
            false,
            true // HttpOnly
        );

        return response('hello world')->withCookie(cookie(self::REFRESH_TOKEN));
        return [
            'access_token' => $data->access_token,
            'expires_in' => $data->expires_in
        ];
    }

}

Cookie::queue() in my proxy() method aren't storing a cookie.

It tried the solution from this stack overflow question

Cookie queuing is not enabled for api requests, this is the reason why it didn't work.

Open file App/Http/Kernel.php add the line \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, in protected $middleware array as displayed in above code snippet and test again it should work now.

This solution didn't work for me.

I don't know how to implement the second solution:

In case anyone fond their way here by Google, one way for cookie inclusion to silently fail is if you're explicitly defining your domain variable in its creation, and forgot to remove the "http://" from the beginning of it first. That's not the case with OP, but it was what brought me here. ;)

Maybe that's it? Any help would be awesome

May
04
2 months ago
Activity icon

Started a new Conversation Can You Guys Make Sure There Isn't A Problem With My Code Before I Reopen An Old Cashier Issue?

I'm having the same issue as this cashier git issue: Changing plans does not change the local subscription name

As the issue you implies:

$this->user->subscription('basic')->swap('premium');

Needs to be changed to this to be functional:

SubscriptionsTest.php

$this->user->subscription('basic')->swap('premium')->update(['name' => 'premium']);

It looks like there was a pull request made and it was merged to fix the bug. So maybe it's just my code:

protected $monthlyPlan;
protected $yearlyPlan;

protected function setUp(): void
{
    parent::setUp();

    $this->monthlyPlan = factory('App\Plan')->create(['name' => 'Monthly', 'stripe_plan_id' => 'plan_number_monthly']); 
    $this->yearlyPlan = factory('App\Plan')->create(['name' => 'Yearly', 'stripe_plan_id' => 'plan_number']);      
}

    /** @test */
public function a_user_can_change_their_subscription()
{
    $this->withoutExceptionHandling();

    Passport::actingAs($user = factory(User::class)->create());

    \Stripe\Stripe::setApiKey(\Config::get('services.stripe.secret'));
    $payment_method = \Stripe\PaymentMethod::create([
        'type' => 'card',
        'card' => [
            'number' => '4242424242424242',
            'exp_month' => 5,
            'exp_year' => 2021,
            'cvc' => '314',
        ],
    ]);

    $user->newSubscription($this->monthlyPlan->name, $this->monthlyPlan->stripe_plan_id)->create($payment_method['id']);

    

    $response = $this->json('POST', '/api/change-subscription', ['old_plan_id'=> $this->monthlyPlan->id, 'new_plan_id' => $this->yearlyPlan->id] )
        ->assertStatus(201);
    
    $this->assertTrue($user->subscribed('Yearly'));
}

api.php

Route::group(['middleware' => 'auth:api'], function() {
    Route::post('change-subscription', 'Api\[email protected]')->name('checkout.process');
});

CheckoutController.php

public function updateSubscription(Request $request)
{
    $newPlan = Plan::findOrFail($request->new_plan_id);
    $oldPlan = Plan::findOrFail($request->old_plan_id);


    try {
        $user = auth()->user();
        
        $user->subscription($oldPlan->name)->swap($newPlan->stripe_plan_id)->update(['name' => $newPlan->name]);

        return response([], 201);
    } catch (\Exception $e) {
        return response([], 500);
    }
}

As you can see, just like the git issues I had to add ->update(['name' => $newPlan->name]); to the end of $user->subscription($oldPlan->name)->swap($newPlan->stripe_plan_id)

I figure I'd just run it by you guys first before reopening the issue. Thanks for any help

Apr
28
2 months ago
Activity icon

Started a new Conversation Can Migrate DB, But On Insert Get: Access Denied For User 'root'@'localhost'

I'm using postman to try and make an API post request:

http://127.0.0.1:8000/api/auth/register?name=corbin&[email protected]&password=password

There must be a database connecting because I can migrate my db just fine. However when I try and create a record I get this:

Illuminate\Database\QueryException: SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: NO) (SQL: insert into users (name, email, password, updated_at, created_at) values (corbin, [email protected], $2y$10$QfVSBr67zOJldKrxqjVVsO9pZ5QV9qOkvoCH./FPRZfSRVo1A4GqC, 2020-04-28 08:53:51, 2020-04-28 08:53:51)) in file /Applications/MAMP/htdocs/laravel-vue-spa/vendor/laravel/framework/src/Illuminate/Database/Connection.php on line 671

Here's my AuthController:

public function register(Request $request)
    {
       
        
        /*$request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|',
            'password' => 'required|string|min:6|confirmed'
        ]);*/

        $user = new User();
        $user->name = $request->name;
        $user->email = $request->email;
        $user->password = bcrypt($request->password);

        $user->save();

        /*if($user->save()){
            return response()->json([
                'message' => 'User was successfully created.',
                'status_code' => 201
            ], 201);
        } else {
            return response()->json([
                'message' => 'An error occured. Please try again.',
                'status_code' => 500
            ], 500);
        } */
    }

.env

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel_spa
DB_USERNAME=root
DB_PASSWORD=password

I also tried switching DB_HOST=localhost

Apr
22
2 months ago
Activity icon

Started a new Conversation How Long Did It Take You To Swap Out Bootstrap For Your Own CSS Or Another CSS Framework In The Spark Blade Templates?

It seems like there are A LOT of blade templates and vue components in the Spark package. I'm wondering if any of you went through the process of actually swapping out bootstrap for something else and how many hours, days, or maybe weeks it took you?

Thanks guys. I know this is a forum where there's suppose to be binary answer, but I can't get much of a response anywhere else.

Feb
29
4 months ago
Activity icon

Started a new Conversation How Do I Redirect A User On Login/registration Based On A Condition On The Authenticated User?

What I'm trying to do is redirect a user if they are a counsellor to one route, if they aren't to another:

SparkServiceProvider.php

public function booted()
{

    if(auth()->user()->is_counsellor){
        Spark::afterLoginRedirectTo('/timeline');
    } else {
        Spark::afterLoginRedirectTo('/thoughts');
    }

}

Error

Trying to get property 'is_counsellor' of non-object

There has to be a way to override/overrule Sparks redirects in a way that takes into consideration conditions on authentication right? I've seen many unanswered threads about this, so I'm pretty confused

Feb
10
5 months ago
Activity icon

Replied to BadMethodCallException Call To Undefined Method On Model Method That References Itself Using Many To Many Relations

@sinnbeck I just went to the file -> save in the drop down to make sure that it was saving.

I wonder if this is because the User is extending SparkUser (I'm using laravel Spark)

class User extends SparkUser { }

Activity icon

Started a new Conversation BadMethodCallException Call To Undefined Method On Model Method That References Itself Using Many To Many Relations

I'm building a system where a user can have many teachers (Same User model) and teachers can have many users. When I hit the controller I get:

BadMethodCallException with message 'Call to undefined method App/User::teachers()

Code

Controller

    public function requestTeacher()
    {
        $teacher = User::where(['hashed_id' => 'auserhash123', 'is_teacher' => 1])->first();
    
        $user = auth()->user()->first();
        
        $teacher->notify(new teacherRequest($user));
    
        $user->teachers()->attach($teacher->id);
    }

User Model

    public function teachers()
    {
        return $this->belongsToMany('App\User','teacher_approval','teacher_id');
    }
    
    public function clients()
    {
        return $this->belongsToMany('App\User','teacher_approval','teacher_id','user_id');
    }

teacher_approval table

    Schema::create('teacher_approval', function (Blueprint $table) {
        $table->unsignedInteger('user_id');
        $table->unsignedInteger('teacher_id');
        $table->unsignedInteger('status')->default(0);
        $table->timestamp('decission_date')->nullable();
    
        $table->foreign('user_id')->references('id')->on('users')
            ->onDelete('cascade');
        $table->foreign('teacher_id')->references('id')->on('users')
            ->onDelete('cascade');
    });
Jan
25
5 months ago
Activity icon

Replied to How Do I Test Encrypted Database Fields?

@bugsysha I think I'm starting to see what you're getting at:

Okay for the assertDatabaseHas I changed that to be assertJsonFragment:

PostController

public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required|max:255',
    ]);
    
    $post = Post::create([
        'title' => $request->input('title'),
    ]);

    return response(new PostResource($post),201);
}

PostTest

/** @test */
public function a_user_can_create_a_post_record()
{
    //$this->withoutExceptionHandling();
    $this->actingAs($creator = factory('App\User')->create());

    $post = factory('App\Post')->make(['user_id' => $creator->id]);

    //You can't use $post->toArray() because toArray() encrypts fields.
    $decryptedPost = [
        'title' => $post->title,
    ];

    $response = $this->json('POST', '/api/posts', $decryptedPost )
        ->assertStatus(201);

    $response->assertJsonFragment(['title' => $post->title]);
}

You got me thinking that I should probably be testing to see if values have been encrypted. That's where using this at the top of my code comes in:

$this->mock(Crypt::class, function ($mock) {
    $mock->shouldReceive('encrypt')->times(1)->andReturn('encrypt-value');
    $mock->shouldReceive('decrypt')->times(1)->andReturn('decrypt-value');
});

Is that true?

Activity icon

Replied to How Do I Test Encrypted Database Fields?

@bugsysha, I'm still moderately confused. I'm trying to read up on mockery and watch videos to catch up, but shouldn't I be mocking the Post model and not the Crypt class? Then after mocking the post model I should receive the methods in the encryptable trait?

Also I would avoid dynamic assertion values for following

 $this->assertDatabaseHas('posts', [
        'title' => $post->title, // replace with a string
    ]);

Since the code might now show you errors that you have with code and it is easier to debug.

The problem is that I don't know what that string is since the title gets encrypted.

Thanks for helping me so far. I swear I'm sitting here reading and watching videos about mockery trying to grasp it. I think I'm getting closer.

Jan
24
5 months ago
Activity icon

Replied to How Do I Test Encrypted Database Fields?

@bugsysha no I haven’t, this is a new concept to me. After reading the docs I’m kinda confused as to how I would implement that? Like would I use object mocking? Is there any pseudo code you can share with me to grasp this?

Activity icon

Started a new Conversation How Do I Test Encrypted Database Fields?

I'm encrypting database fields using a custom Encryptable trait:

Encryptable.php

<?php
namespace App;

use Illuminate\Support\Facades\Crypt;
trait Encryptable
{
    public function getAttribute($key)
    {
        $value = parent::getAttribute($key);

        if (in_array($key, $this->encryptable)) {
            $value = Crypt::decrypt($value);
            return $value;
        } else {
            return $value;
        }
    }

    public function setAttribute($key, $value)
    {
        if (in_array($key, $this->encryptable)) {
            $value = Crypt::encrypt($value);
        }

        return parent::setAttribute($key, $value);
    }
} 

This allows me to specify in my model what fields I would like encrypted:

Post

use Encryptable;

protected $encryptable = ['title'];

I've learned that Crypt doesn't create a reproducible hash which makes it problematic in my assertDatabaseHas() assertions:

CreatePostTest.php

/** @test */
public function a_user_can_create_a_thought_record()
{
    //$this->withoutExceptionHandling();
    $this->actingAs($creator = factory('App\User')->create());

    $post = factory('App\Post')->make(['user_id' => $creator->id]);

    //You can't use $post->toArray() because toArray() encrypts fields.
    $decryptedPost = [
        'title' => $post->title,
    ];

    $response = $this->json('POST', '/api/posts', $decryptedPost )
        ->assertStatus(201);

    $this->assertDatabaseHas('posts', [
        'title' => $post->title,
    ]);
}

As said this doesn't work:

$this->assertDatabaseHas('posts', [
        'title' => $post->title,
 ]);

What other test can I use to assert that the posts title is the same in the database as what I'm sending through the request and how do I do this?